1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26 
27 #include "config.h"
28 
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
34 #endif
35 #include <stdlib.h>
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #include <string.h>
40 #include <errno.h>
41 #ifdef HAVE_PWD_H
42 #include <pwd.h>
43 #endif
44 
45 #include <glib.h>		/* Include early to get G_OS_WIN32 etc */
46 #include <glib/gstdio.h>
47 
48 #if defined(G_PLATFORM_WIN32)
49 #include <ctype.h>
50 #define STRICT
51 #include <windows.h>
52 #undef STRICT
53 #endif /* G_PLATFORM_WIN32 */
54 
55 #include "gdk/gdkkeysyms.h"
56 
57 #undef GTK_DISABLE_DEPRECATED /* GtkOptionMenu */
58 
59 #include "gtkbutton.h"
60 #include "gtkcellrenderertext.h"
61 #include "gtkentry.h"
62 #include "gtkfilesel.h"
63 #include "gtkhbox.h"
64 #include "gtkhbbox.h"
65 #include "gtkintl.h"
66 #include "gtklabel.h"
67 #include "gtkliststore.h"
68 #include "gtkmain.h"
69 #include "gtkprivate.h"
70 #include "gtkscrolledwindow.h"
71 #include "gtkstock.h"
72 #include "gtktreeselection.h"
73 #include "gtktreeview.h"
74 #include "gtkvbox.h"
75 #include "gtkmenu.h"
76 #include "gtkmenuitem.h"
77 #include "gtkdialog.h"
78 #include "gtkmessagedialog.h"
79 #include "gtkdnd.h"
80 #include "gtkeventbox.h"
81 #include "gtkoptionmenu.h"
82 
83 #define WANT_HPANED 1
84 #include "gtkhpaned.h"
85 
86 #include "gtkalias.h"
87 
88 #ifdef G_OS_WIN32
89 #include <direct.h>
90 #include <io.h>
91 #ifndef S_ISDIR
92 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
93 #endif
94 #endif /* G_OS_WIN32 */
95 
96 #ifdef G_WITH_CYGWIN
97 #include <sys/cygwin.h>		/* For cygwin_conv_to_posix_path */
98 #endif
99 
100 #define DIR_LIST_WIDTH   180
101 #define DIR_LIST_HEIGHT  180
102 #define FILE_LIST_WIDTH  180
103 #define FILE_LIST_HEIGHT 180
104 
105 /* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
106  * in here, since the rest of the code in the file does require some
107  * fixed maximum.
108  */
109 #ifndef MAXPATHLEN
110 #  ifdef PATH_MAX
111 #    define MAXPATHLEN PATH_MAX
112 #  else
113 #    define MAXPATHLEN 2048
114 #  endif
115 #endif
116 
117 /* I've put this here so it doesn't get confused with the
118  * file completion interface */
119 typedef struct _HistoryCallbackArg HistoryCallbackArg;
120 
121 struct _HistoryCallbackArg
122 {
123   gchar *directory;
124   GtkWidget *menu_item;
125 };
126 
127 
128 typedef struct _CompletionState    CompletionState;
129 typedef struct _CompletionDir      CompletionDir;
130 typedef struct _CompletionDirSent  CompletionDirSent;
131 typedef struct _CompletionDirEntry CompletionDirEntry;
132 typedef struct _CompletionUserDir  CompletionUserDir;
133 typedef struct _PossibleCompletion PossibleCompletion;
134 
135 /* Non-external file completion decls and structures */
136 
137 /* A contant telling PRCS how many directories to cache.  Its actually
138  * kept in a list, so the geometry isn't important. */
139 #define CMPL_DIRECTORY_CACHE_SIZE 10
140 
141 /* A constant used to determine whether a substring was an exact
142  * match by first_diff_index()
143  */
144 #define PATTERN_MATCH -1
145 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
146 #define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
147 
148 /* This structure contains all the useful information about a directory
149  * for the purposes of filename completion.  These structures are cached
150  * in the CompletionState struct.  CompletionDir's are reference counted.
151  */
152 struct _CompletionDirSent
153 {
154 #ifndef G_PLATFORM_WIN32
155   ino_t inode;
156   time_t mtime;
157   dev_t device;
158 #endif
159 
160   gint entry_count;
161   struct _CompletionDirEntry *entries;
162 };
163 
164 struct _CompletionDir
165 {
166   CompletionDirSent *sent;
167 
168   gchar *fullname;
169   gint fullname_len;
170 
171   struct _CompletionDir *cmpl_parent;
172   gint cmpl_index;
173   gchar *cmpl_text;
174 };
175 
176 /* This structure contains pairs of directory entry names with a flag saying
177  * whether or not they are a valid directory.  NOTE: This information is used
178  * to provide the caller with information about whether to update its completions
179  * or try to open a file.  Since directories are cached by the directory mtime,
180  * a symlink which points to an invalid file (which will not be a directory),
181  * will not be reevaluated if that file is created, unless the containing
182  * directory is touched.  I consider this case to be worth ignoring (josh).
183  */
184 struct _CompletionDirEntry
185 {
186   gboolean is_dir;
187   gchar *entry_name;
188   gchar *sort_key;
189 };
190 
191 struct _CompletionUserDir
192 {
193   gchar *login;
194   gchar *homedir;
195 };
196 
197 struct _PossibleCompletion
198 {
199   /* accessible fields, all are accessed externally by functions
200    * declared above
201    */
202   gchar *text;
203   gint is_a_completion;
204   gboolean is_directory;
205 
206   /* Private fields
207    */
208   gint text_alloc;
209 };
210 
211 struct _CompletionState
212 {
213   gint last_valid_char;
214   gchar *updated_text;
215   gint updated_text_len;
216   gint updated_text_alloc;
217   gboolean re_complete;
218 
219   gchar *user_dir_name_buffer;
220   gint user_directories_len;
221 
222   gchar *last_completion_text;
223 
224   gint user_completion_index; /* if >= 0, currently completing ~user */
225 
226   struct _CompletionDir *completion_dir; /* directory completing from */
227   struct _CompletionDir *active_completion_dir;
228 
229   struct _PossibleCompletion the_completion;
230 
231   struct _CompletionDir *reference_dir; /* initial directory */
232 
233   GList* directory_storage;
234   GList* directory_sent_storage;
235 
236   struct _CompletionUserDir *user_directories;
237 };
238 
239 enum {
240   PROP_0,
241   PROP_SHOW_FILEOPS,
242   PROP_FILENAME,
243   PROP_SELECT_MULTIPLE
244 };
245 
246 enum {
247   DIR_COLUMN
248 };
249 
250 enum {
251   FILE_COLUMN
252 };
253 
254 /* File completion functions which would be external, were they used
255  * outside of this file.
256  */
257 
258 static CompletionState*    cmpl_init_state        (void);
259 static void                cmpl_free_state        (CompletionState *cmpl_state);
260 static gint                cmpl_state_okay        (CompletionState* cmpl_state);
261 static const gchar*        cmpl_strerror          (gint);
262 
263 static PossibleCompletion* cmpl_completion_matches(gchar           *text_to_complete,
264 						   gchar          **remaining_text,
265 						   CompletionState *cmpl_state);
266 
267 /* Returns a name for consideration, possibly a completion, this name
268  * will be invalid after the next call to cmpl_next_completion.
269  */
270 static char*               cmpl_this_completion   (PossibleCompletion*);
271 
272 /* True if this completion matches the given text.  Otherwise, this
273  * output can be used to have a list of non-completions.
274  */
275 static gint                cmpl_is_a_completion   (PossibleCompletion*);
276 
277 /* True if the completion is a directory
278  */
279 static gboolean            cmpl_is_directory      (PossibleCompletion*);
280 
281 /* Obtains the next completion, or NULL
282  */
283 static PossibleCompletion* cmpl_next_completion   (CompletionState*);
284 
285 /* Updating completions: the return value of cmpl_updated_text() will
286  * be text_to_complete completed as much as possible after the most
287  * recent call to cmpl_completion_matches.  For the present
288  * application, this is the suggested replacement for the user's input
289  * string.  You must CALL THIS AFTER ALL cmpl_text_completions have
290  * been received.
291  */
292 static gchar*              cmpl_updated_text       (CompletionState* cmpl_state);
293 
294 /* After updating, to see if the completion was a directory, call
295  * this.  If it was, you should consider re-calling completion_matches.
296  */
297 static gboolean            cmpl_updated_dir        (CompletionState* cmpl_state);
298 
299 /* Current location: if using file completion, return the current
300  * directory, from which file completion begins.  More specifically,
301  * the cwd concatenated with all exact completions up to the last
302  * directory delimiter('/').
303  */
304 static gchar*              cmpl_reference_position (CompletionState* cmpl_state);
305 
306 #if 0
307 /* This doesn't work currently and would require changes
308  * to fnmatch.c to get working.
309  */
310 /* backing up: if cmpl_completion_matches returns NULL, you may query
311  * the index of the last completable character into cmpl_updated_text.
312  */
313 static gint                cmpl_last_valid_char    (CompletionState* cmpl_state);
314 #endif
315 
316 /* When the user selects a non-directory, call cmpl_completion_fullname
317  * to get the full name of the selected file.
318  */
319 static gchar*              cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
320 
321 
322 /* Directory operations. */
323 static CompletionDir* open_ref_dir         (gchar* text_to_complete,
324 					    gchar** remaining_text,
325 					    CompletionState* cmpl_state);
326 #ifndef G_PLATFORM_WIN32
327 static gboolean       check_dir            (gchar *dir_name,
328 					    GStatBuf *result,
329 					    gboolean *stat_subdirs);
330 #endif
331 static CompletionDir* open_dir             (gchar* dir_name,
332 					    CompletionState* cmpl_state);
333 #ifdef HAVE_PWD_H
334 static CompletionDir* open_user_dir        (const gchar* text_to_complete,
335 					    CompletionState *cmpl_state);
336 #endif
337 static CompletionDir* open_relative_dir    (gchar* dir_name, CompletionDir* dir,
338 					    CompletionState *cmpl_state);
339 static CompletionDirSent* open_new_dir     (gchar* dir_name,
340 					    GStatBuf *sbuf,
341 					    gboolean stat_subdirs);
342 static gint           correct_dir_fullname (CompletionDir* cmpl_dir);
343 static gint           correct_parent       (CompletionDir* cmpl_dir,
344 					    GStatBuf *sbuf);
345 #ifndef G_PLATFORM_WIN32
346 static gchar*         find_parent_dir_fullname    (gchar* dirname);
347 #endif
348 static CompletionDir* attach_dir           (CompletionDirSent* sent,
349 					    gchar* dir_name,
350 					    CompletionState *cmpl_state);
351 static void           free_dir_sent (CompletionDirSent* sent);
352 static void           free_dir      (CompletionDir  *dir);
353 static void           prune_memory_usage(CompletionState *cmpl_state);
354 
355 /* Completion operations */
356 #ifdef HAVE_PWD_H
357 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
358 						      CompletionState *cmpl_state);
359 #endif
360 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
361 static CompletionDir* find_completion_dir(gchar* text_to_complete,
362 					  gchar** remaining_text,
363 					  CompletionState* cmpl_state);
364 static PossibleCompletion* append_completion_text(gchar* text,
365 						  CompletionState* cmpl_state);
366 #ifdef HAVE_PWD_H
367 static gint get_pwdb(CompletionState* cmpl_state);
368 static gint compare_user_dir(const void* a, const void* b);
369 #endif
370 static gint first_diff_index(gchar* pat, gchar* text);
371 static gint compare_cmpl_dir(const void* a, const void* b);
372 static void update_cmpl(PossibleCompletion* poss,
373 			CompletionState* cmpl_state);
374 
375 static void gtk_file_selection_set_property  (GObject         *object,
376 					      guint            prop_id,
377 					      const GValue    *value,
378 					      GParamSpec      *pspec);
379 static void gtk_file_selection_get_property  (GObject         *object,
380 					      guint            prop_id,
381 					      GValue          *value,
382 					      GParamSpec      *pspec);
383 static void gtk_file_selection_finalize      (GObject               *object);
384 static void gtk_file_selection_destroy       (GtkObject             *object);
385 static void gtk_file_selection_map           (GtkWidget             *widget);
386 static gint gtk_file_selection_key_press     (GtkWidget             *widget,
387 					      GdkEventKey           *event,
388 					      gpointer               user_data);
389 static gint gtk_file_selection_insert_text   (GtkWidget             *widget,
390 					      const gchar           *new_text,
391 					      gint                   new_text_length,
392 					      gint                  *position,
393 					      gpointer               user_data);
394 static void gtk_file_selection_update_fileops (GtkFileSelection     *filesel);
395 
396 static void gtk_file_selection_file_activate (GtkTreeView       *tree_view,
397 					      GtkTreePath       *path,
398 					      GtkTreeViewColumn *column,
399 					      gpointer           user_data);
400 static void gtk_file_selection_file_changed  (GtkTreeSelection  *selection,
401 					      gpointer           user_data);
402 static void gtk_file_selection_dir_activate  (GtkTreeView       *tree_view,
403 					      GtkTreePath       *path,
404 					      GtkTreeViewColumn *column,
405 					      gpointer           user_data);
406 
407 static void gtk_file_selection_populate      (GtkFileSelection      *fs,
408 					      gchar                 *rel_path,
409 					      gboolean               try_complete,
410 					      gboolean               reset_entry);
411 static void gtk_file_selection_abort         (GtkFileSelection      *fs);
412 
413 static void gtk_file_selection_update_history_menu (GtkFileSelection       *fs,
414 						    gchar                  *current_dir);
415 
416 static void gtk_file_selection_create_dir  (GtkWidget *widget, gpointer data);
417 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
418 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
419 
420 static void free_selected_names (GPtrArray *names);
421 
422 #ifndef G_PLATFORM_WIN32
423 
424 #define compare_utf8_filenames(a, b) strcmp(a, b)
425 #define compare_sys_filenames(a, b) strcmp(a, b)
426 
427 #else
428 
429 static gint
compare_utf8_filenames(const gchar * a,const gchar * b)430 compare_utf8_filenames (const gchar *a,
431 			const gchar *b)
432 {
433   gchar *a_folded, *b_folded;
434   gint retval;
435 
436   a_folded = g_utf8_strdown (a, -1);
437   b_folded = g_utf8_strdown (b, -1);
438 
439   retval = strcmp (a_folded, b_folded);
440 
441   g_free (a_folded);
442   g_free (b_folded);
443 
444   return retval;
445 }
446 
447 static gint
compare_sys_filenames(const gchar * a,const gchar * b)448 compare_sys_filenames (const gchar *a,
449 		       const gchar *b)
450 {
451   gchar *a_utf8, *b_utf8;
452   gint retval;
453 
454   a_utf8 = g_filename_to_utf8 (a, -1, NULL, NULL, NULL);
455   b_utf8 = g_filename_to_utf8 (b, -1, NULL, NULL, NULL);
456 
457   retval = compare_utf8_filenames (a_utf8, b_utf8);
458 
459   g_free (a_utf8);
460   g_free (b_utf8);
461 
462   return retval;
463 }
464 
465 #endif
466 
467 /* Saves errno when something cmpl does fails. */
468 static gint cmpl_errno;
469 
470 #ifdef G_WITH_CYGWIN
471 /*
472  * Take the path currently in the file selection
473  * entry field and translate as necessary from
474  * a Win32 style to Cygwin style path.  For
475  * instance translate:
476  * x:\somepath\file.jpg
477  * to:
478  * /cygdrive/x/somepath/file.jpg
479  *
480  * Replace the path in the selection text field.
481  * Return a boolean value concerning whether a
482  * translation had to be made.
483  */
484 static int
translate_win32_path(GtkFileSelection * filesel)485 translate_win32_path (GtkFileSelection *filesel)
486 {
487   int updated = 0;
488   const gchar *path;
489   gchar newPath[MAX_PATH];
490 
491   /*
492    * Retrieve the current path
493    */
494   path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
495 
496   cygwin_conv_to_posix_path (path, newPath);
497   updated = (strcmp (path, newPath) != 0);
498 
499   if (updated)
500     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
501 
502   return updated;
503 }
504 #endif
505 
G_DEFINE_TYPE(GtkFileSelection,gtk_file_selection,GTK_TYPE_DIALOG)506 G_DEFINE_TYPE (GtkFileSelection, gtk_file_selection, GTK_TYPE_DIALOG)
507 
508 static void
509 gtk_file_selection_class_init (GtkFileSelectionClass *class)
510 {
511   GObjectClass *gobject_class;
512   GtkObjectClass *object_class;
513   GtkWidgetClass *widget_class;
514 
515   gobject_class = (GObjectClass*) class;
516   object_class = (GtkObjectClass*) class;
517   widget_class = (GtkWidgetClass*) class;
518 
519   gobject_class->finalize = gtk_file_selection_finalize;
520   gobject_class->set_property = gtk_file_selection_set_property;
521   gobject_class->get_property = gtk_file_selection_get_property;
522 
523   g_object_class_install_property (gobject_class,
524                                    PROP_FILENAME,
525                                    g_param_spec_string ("filename",
526                                                         P_("Filename"),
527                                                         P_("The currently selected filename"),
528                                                         NULL,
529                                                         GTK_PARAM_READWRITE));
530   g_object_class_install_property (gobject_class,
531 				   PROP_SHOW_FILEOPS,
532 				   g_param_spec_boolean ("show-fileops",
533 							 P_("Show file operations"),
534 							 P_("Whether buttons for creating/manipulating files should be displayed"),
535 							 TRUE,
536 							 GTK_PARAM_READWRITE));
537   g_object_class_install_property (gobject_class,
538 				   PROP_SELECT_MULTIPLE,
539 				   g_param_spec_boolean ("select-multiple",
540 							 P_("Select Multiple"),
541 							 P_("Whether to allow multiple files to be selected"),
542 							 FALSE,
543 							 GTK_PARAM_READWRITE));
544   object_class->destroy = gtk_file_selection_destroy;
545   widget_class->map = gtk_file_selection_map;
546 }
547 
gtk_file_selection_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)548 static void gtk_file_selection_set_property (GObject         *object,
549 					     guint            prop_id,
550 					     const GValue    *value,
551 					     GParamSpec      *pspec)
552 {
553   GtkFileSelection *filesel;
554 
555   filesel = GTK_FILE_SELECTION (object);
556 
557   switch (prop_id)
558     {
559     case PROP_FILENAME:
560       gtk_file_selection_set_filename (filesel,
561                                        g_value_get_string (value));
562       break;
563     case PROP_SHOW_FILEOPS:
564       if (g_value_get_boolean (value))
565 	 gtk_file_selection_show_fileop_buttons (filesel);
566       else
567 	 gtk_file_selection_hide_fileop_buttons (filesel);
568       break;
569     case PROP_SELECT_MULTIPLE:
570       gtk_file_selection_set_select_multiple (filesel, g_value_get_boolean (value));
571       break;
572     default:
573       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
574       break;
575     }
576 }
577 
gtk_file_selection_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)578 static void gtk_file_selection_get_property (GObject         *object,
579 					     guint            prop_id,
580 					     GValue          *value,
581 					     GParamSpec      *pspec)
582 {
583   GtkFileSelection *filesel;
584 
585   filesel = GTK_FILE_SELECTION (object);
586 
587   switch (prop_id)
588     {
589     case PROP_FILENAME:
590       g_value_set_string (value,
591                           gtk_file_selection_get_filename(filesel));
592       break;
593 
594     case PROP_SHOW_FILEOPS:
595       /* This is a little bit hacky, but doing otherwise would require
596        * adding a field to the object.
597        */
598       g_value_set_boolean (value, (filesel->fileop_c_dir &&
599 				   filesel->fileop_del_file &&
600 				   filesel->fileop_ren_file));
601       break;
602     case PROP_SELECT_MULTIPLE:
603       g_value_set_boolean (value, gtk_file_selection_get_select_multiple (filesel));
604       break;
605     default:
606       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
607       break;
608     }
609 }
610 
611 static gboolean
grab_default(GtkWidget * widget)612 grab_default (GtkWidget *widget)
613 {
614   gtk_widget_grab_default (widget);
615   return FALSE;
616 }
617 
618 static void
gtk_file_selection_init(GtkFileSelection * filesel)619 gtk_file_selection_init (GtkFileSelection *filesel)
620 {
621   GtkWidget *entry_vbox;
622   GtkWidget *label;
623   GtkWidget *list_hbox, *list_container;
624   GtkWidget *pulldown_hbox;
625   GtkWidget *scrolled_win;
626   GtkWidget *eventbox;
627   GtkWidget *spacer;
628   GtkDialog *dialog;
629 
630   GtkListStore *model;
631   GtkTreeViewColumn *column;
632 
633   gtk_widget_push_composite_child ();
634 
635   dialog = GTK_DIALOG (filesel);
636 
637   filesel->cmpl_state = cmpl_init_state ();
638 
639   /* The dialog-sized vertical box  */
640   filesel->main_vbox = dialog->vbox;
641   gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
642 
643   /* The horizontal box containing create, rename etc. buttons */
644   filesel->button_area = gtk_hbutton_box_new ();
645   gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
646   gtk_box_set_spacing (GTK_BOX (filesel->button_area), 0);
647   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area,
648 		      FALSE, FALSE, 0);
649   gtk_widget_show (filesel->button_area);
650 
651   gtk_file_selection_show_fileop_buttons (filesel);
652 
653   /* hbox for pulldown menu */
654   pulldown_hbox = gtk_hbox_new (TRUE, 5);
655   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
656   gtk_widget_show (pulldown_hbox);
657 
658   /* Pulldown menu */
659   filesel->history_pulldown = gtk_option_menu_new ();
660   gtk_widget_show (filesel->history_pulldown);
661   gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown,
662 		      FALSE, FALSE, 0);
663 
664   /*  The horizontal box containing the directory and file listboxes  */
665 
666   spacer = gtk_hbox_new (FALSE, 0);
667   gtk_widget_set_size_request (spacer, -1, 5);
668   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);
669   gtk_widget_show (spacer);
670 
671   list_hbox = gtk_hbox_new (FALSE, 5);
672   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
673   gtk_widget_show (list_hbox);
674   if (WANT_HPANED)
675     list_container = g_object_new (GTK_TYPE_HPANED,
676 				   "visible", TRUE,
677 				   "parent", list_hbox,
678 				   "border_width", 0,
679 				   NULL);
680   else
681     list_container = list_hbox;
682 
683   spacer = gtk_hbox_new (FALSE, 0);
684   gtk_widget_set_size_request (spacer, -1, 5);
685   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);
686   gtk_widget_show (spacer);
687 
688   /* The directories list */
689 
690   model = gtk_list_store_new (1, G_TYPE_STRING);
691   filesel->dir_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
692   g_object_unref (model);
693 
694   column = gtk_tree_view_column_new_with_attributes (_("Folders"),
695 						     gtk_cell_renderer_text_new (),
696 						     "text", DIR_COLUMN,
697 						     NULL);
698   label = gtk_label_new_with_mnemonic (_("Fol_ders"));
699   gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->dir_list);
700   gtk_widget_show (label);
701   gtk_tree_view_column_set_widget (column, label);
702   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
703   gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->dir_list), column);
704 
705   gtk_widget_set_size_request (filesel->dir_list,
706 			       DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
707   g_signal_connect (filesel->dir_list, "row-activated",
708 		    G_CALLBACK (gtk_file_selection_dir_activate), filesel);
709 
710   /*  gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list)); */
711 
712   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
713   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
714   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
715   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
716 				  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
717   gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
718   if (GTK_IS_PANED (list_container))
719     gtk_paned_pack1 (GTK_PANED (list_container), scrolled_win, TRUE, TRUE);
720   else
721     gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
722   gtk_widget_show (filesel->dir_list);
723   gtk_widget_show (scrolled_win);
724 
725   /* The files list */
726   model = gtk_list_store_new (1, G_TYPE_STRING);
727   filesel->file_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
728   g_object_unref (model);
729 
730   column = gtk_tree_view_column_new_with_attributes (_("Files"),
731 						     gtk_cell_renderer_text_new (),
732 						     "text", FILE_COLUMN,
733 						     NULL);
734   label = gtk_label_new_with_mnemonic (_("_Files"));
735   gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->file_list);
736   gtk_widget_show (label);
737   gtk_tree_view_column_set_widget (column, label);
738   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
739   gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->file_list), column);
740 
741   gtk_widget_set_size_request (filesel->file_list,
742 			       FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
743   g_signal_connect (filesel->file_list, "row-activated",
744 		    G_CALLBACK (gtk_file_selection_file_activate), filesel);
745   g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list)), "changed",
746 		    G_CALLBACK (gtk_file_selection_file_changed), filesel);
747 
748   /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list)); */
749 
750   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
751   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
752   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
753   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
754 				  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
755   gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
756   gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
757   gtk_widget_show (filesel->file_list);
758   gtk_widget_show (scrolled_win);
759 
760   /* action area for packing buttons into. */
761   filesel->action_area = gtk_hbox_new (TRUE, 0);
762   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area,
763 		      FALSE, FALSE, 0);
764   gtk_widget_show (filesel->action_area);
765 
766   /*  The OK/Cancel button area */
767 
768   /*  The Cancel button  */
769   filesel->cancel_button = gtk_dialog_add_button (dialog,
770                                                   GTK_STOCK_CANCEL,
771                                                   GTK_RESPONSE_CANCEL);
772   /*  The OK button  */
773   filesel->ok_button = gtk_dialog_add_button (dialog,
774                                               GTK_STOCK_OK,
775                                               GTK_RESPONSE_OK);
776 
777   gtk_dialog_set_alternative_button_order (dialog,
778 					   GTK_RESPONSE_OK,
779 					   GTK_RESPONSE_CANCEL,
780 					   -1);
781 
782   gtk_widget_grab_default (filesel->ok_button);
783 
784   /*  The selection entry widget  */
785   entry_vbox = gtk_vbox_new (FALSE, 2);
786   gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
787   gtk_widget_show (entry_vbox);
788 
789   eventbox = gtk_event_box_new ();
790   filesel->selection_text = label = gtk_label_new ("");
791   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
792   gtk_container_add (GTK_CONTAINER (eventbox), label);
793   gtk_box_pack_start (GTK_BOX (entry_vbox), eventbox, FALSE, FALSE, 0);
794   gtk_widget_show (label);
795   gtk_widget_show (eventbox);
796 
797   filesel->selection_entry = gtk_entry_new ();
798   g_signal_connect (filesel->selection_entry, "key-press-event",
799 		    G_CALLBACK (gtk_file_selection_key_press), filesel);
800   g_signal_connect (filesel->selection_entry, "insert-text",
801 		    G_CALLBACK (gtk_file_selection_insert_text), NULL);
802   g_signal_connect_swapped (filesel->selection_entry, "changed",
803 			    G_CALLBACK (gtk_file_selection_update_fileops), filesel);
804   g_signal_connect_swapped (filesel->selection_entry, "focus-in-event",
805 			    G_CALLBACK (grab_default),
806 			    filesel->ok_button);
807   g_signal_connect_swapped (filesel->selection_entry, "activate",
808 			    G_CALLBACK (gtk_button_clicked),
809 			    filesel->ok_button);
810 
811   gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
812   gtk_widget_show (filesel->selection_entry);
813 
814   gtk_label_set_mnemonic_widget (GTK_LABEL (filesel->selection_text),
815 				 filesel->selection_entry);
816 
817   if (!cmpl_state_okay (filesel->cmpl_state))
818     {
819       gchar err_buf[256];
820 
821       g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
822 
823       gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
824     }
825   else
826     {
827       gtk_file_selection_populate (filesel, "", FALSE, TRUE);
828     }
829 
830   gtk_widget_grab_focus (filesel->selection_entry);
831 
832   gtk_widget_pop_composite_child ();
833 }
834 
835 static void
dnd_really_drop(GtkWidget * dialog,gint response_id,GtkFileSelection * fs)836 dnd_really_drop  (GtkWidget *dialog, gint response_id, GtkFileSelection *fs)
837 {
838   gchar *filename;
839 
840   if (response_id == GTK_RESPONSE_YES)
841     {
842       filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename");
843 
844       gtk_file_selection_set_filename (fs, filename);
845     }
846 
847   gtk_widget_destroy (dialog);
848 }
849 
850 static void
filenames_dropped(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)851 filenames_dropped (GtkWidget        *widget,
852 		   GdkDragContext   *context,
853 		   gint              x,
854 		   gint              y,
855 		   GtkSelectionData *selection_data,
856 		   guint             info,
857 		   guint             time)
858 {
859   char **uris = NULL;
860   char *filename = NULL;
861   char *hostname;
862   const char *this_hostname;
863   GError *error = NULL;
864 
865   uris = gtk_selection_data_get_uris (selection_data);
866   if (!uris || !uris[0])
867     {
868       g_strfreev (uris);
869       return;
870     }
871 
872   filename = g_filename_from_uri (uris[0], &hostname, &error);
873   g_strfreev (uris);
874 
875   if (!filename)
876     {
877       g_warning ("Error getting dropped filename: %s\n",
878 		 error->message);
879       g_error_free (error);
880       return;
881     }
882 
883   this_hostname = g_get_host_name ();
884 
885   if ((hostname == NULL) ||
886       (strcmp (hostname, this_hostname) == 0) ||
887       (strcmp (hostname, "localhost") == 0))
888     gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget),
889 				     filename);
890   else
891     {
892       GtkWidget *dialog;
893       gchar *filename_utf8;
894 
895       /* Conversion back to UTF-8 should always succeed for the result
896        * of g_filename_from_uri()
897        */
898       filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
899       g_assert (filename_utf8);
900 
901       dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
902 				       GTK_DIALOG_DESTROY_WITH_PARENT,
903 				       GTK_MESSAGE_QUESTION,
904 				       GTK_BUTTONS_YES_NO,
905 				       _("The file \"%s\" resides on another machine (called %s) and may not be available to this program.\n"
906 					 "Are you sure that you want to select it?"), filename_utf8, hostname);
907       g_free (filename_utf8);
908 
909       g_object_set_data_full (G_OBJECT (dialog), I_("gtk-fs-dnd-filename"), g_strdup (filename), g_free);
910 
911       g_signal_connect_data (dialog, "response",
912 			     (GCallback) dnd_really_drop,
913 			     widget, NULL, 0);
914 
915       gtk_widget_show (dialog);
916     }
917 
918   g_free (hostname);
919   g_free (filename);
920 }
921 
922 static void
filenames_drag_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time,GtkFileSelection * filesel)923 filenames_drag_get (GtkWidget        *widget,
924 		    GdkDragContext   *context,
925 		    GtkSelectionData *selection_data,
926 		    guint             info,
927 		    guint             time,
928 		    GtkFileSelection *filesel)
929 {
930   const gchar *file;
931   gchar *filename_utf8;
932 
933   file = gtk_file_selection_get_filename (filesel);
934   if (!file)
935     return;
936 
937   if (gtk_targets_include_uri (&selection_data->target, 1))
938     {
939       gchar *file_uri;
940       const char *hostname;
941       GError *error;
942       char *uris[2];
943 
944       hostname = g_get_host_name ();
945 
946       error = NULL;
947       file_uri = g_filename_to_uri (file, hostname, &error);
948       if (!file_uri)
949         {
950           g_warning ("Error getting filename: %s\n",
951                       error->message);
952           g_error_free (error);
953           return;
954         }
955 
956       uris[0] = file_uri;
957       uris[1] = NULL;
958       gtk_selection_data_set_uris (selection_data, uris);
959       g_free (file_uri);
960 
961       return;
962     }
963 
964   filename_utf8 = g_filename_to_utf8 (file, -1, NULL, NULL, NULL);
965   if (!filename_utf8)
966     return;
967 
968   gtk_selection_data_set_text (selection_data, filename_utf8, -1);
969   g_free (filename_utf8);
970 }
971 
972 static void
file_selection_setup_dnd(GtkFileSelection * filesel)973 file_selection_setup_dnd (GtkFileSelection *filesel)
974 {
975   GtkWidget *eventbox;
976 
977   gtk_drag_dest_set (GTK_WIDGET (filesel),
978 		     GTK_DEST_DEFAULT_ALL,
979 		     NULL, 0,
980 		     GDK_ACTION_COPY);
981   gtk_drag_dest_add_uri_targets (GTK_WIDGET (filesel));
982 
983   g_signal_connect (filesel, "drag-data-received",
984 		    G_CALLBACK (filenames_dropped), NULL);
985 
986   eventbox = gtk_widget_get_parent (filesel->selection_text);
987   gtk_drag_source_set (eventbox,
988 		       GDK_BUTTON1_MASK,
989 		       NULL, 0,
990 		       GDK_ACTION_COPY);
991   gtk_drag_source_add_uri_targets (eventbox);
992   gtk_drag_source_add_text_targets (eventbox);
993 
994   g_signal_connect (eventbox, "drag-data-get",
995 		    G_CALLBACK (filenames_drag_get), filesel);
996 }
997 
998 GtkWidget*
gtk_file_selection_new(const gchar * title)999 gtk_file_selection_new (const gchar *title)
1000 {
1001   GtkFileSelection *filesel;
1002 
1003   filesel = g_object_new (GTK_TYPE_FILE_SELECTION, NULL);
1004   gtk_window_set_title (GTK_WINDOW (filesel), title);
1005   gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
1006 
1007   file_selection_setup_dnd (filesel);
1008 
1009   return GTK_WIDGET (filesel);
1010 }
1011 
1012 void
gtk_file_selection_show_fileop_buttons(GtkFileSelection * filesel)1013 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
1014 {
1015   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1016 
1017   /* delete, create directory, and rename */
1018   if (!filesel->fileop_c_dir)
1019     {
1020       filesel->fileop_c_dir = gtk_button_new_with_mnemonic (_("_New Folder"));
1021       g_signal_connect (filesel->fileop_c_dir, "clicked",
1022 			G_CALLBACK (gtk_file_selection_create_dir),
1023 			filesel);
1024       gtk_box_pack_start (GTK_BOX (filesel->button_area),
1025 			  filesel->fileop_c_dir, TRUE, TRUE, 0);
1026       gtk_widget_show (filesel->fileop_c_dir);
1027     }
1028 
1029   if (!filesel->fileop_del_file)
1030     {
1031       filesel->fileop_del_file = gtk_button_new_with_mnemonic (_("De_lete File"));
1032       g_signal_connect (filesel->fileop_del_file, "clicked",
1033 			G_CALLBACK (gtk_file_selection_delete_file),
1034 			filesel);
1035       gtk_box_pack_start (GTK_BOX (filesel->button_area),
1036 			  filesel->fileop_del_file, TRUE, TRUE, 0);
1037       gtk_widget_show (filesel->fileop_del_file);
1038     }
1039 
1040   if (!filesel->fileop_ren_file)
1041     {
1042       filesel->fileop_ren_file = gtk_button_new_with_mnemonic (_("_Rename File"));
1043       g_signal_connect (filesel->fileop_ren_file, "clicked",
1044 			G_CALLBACK (gtk_file_selection_rename_file),
1045 			filesel);
1046       gtk_box_pack_start (GTK_BOX (filesel->button_area),
1047 			  filesel->fileop_ren_file, TRUE, TRUE, 0);
1048       gtk_widget_show (filesel->fileop_ren_file);
1049     }
1050 
1051   gtk_file_selection_update_fileops (filesel);
1052 
1053   g_object_notify (G_OBJECT (filesel), "show-fileops");
1054 }
1055 
1056 void
gtk_file_selection_hide_fileop_buttons(GtkFileSelection * filesel)1057 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
1058 {
1059   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1060 
1061   if (filesel->fileop_ren_file)
1062     {
1063       gtk_widget_destroy (filesel->fileop_ren_file);
1064       filesel->fileop_ren_file = NULL;
1065     }
1066 
1067   if (filesel->fileop_del_file)
1068     {
1069       gtk_widget_destroy (filesel->fileop_del_file);
1070       filesel->fileop_del_file = NULL;
1071     }
1072 
1073   if (filesel->fileop_c_dir)
1074     {
1075       gtk_widget_destroy (filesel->fileop_c_dir);
1076       filesel->fileop_c_dir = NULL;
1077     }
1078   g_object_notify (G_OBJECT (filesel), "show-fileops");
1079 }
1080 
1081 
1082 
1083 /**
1084  * gtk_file_selection_set_filename:
1085  * @filesel: a #GtkFileSelection.
1086  * @filename:  a string to set as the default file name.
1087  *
1088  * Sets a default path for the file requestor. If @filename includes a
1089  * directory path, then the requestor will open with that path as its
1090  * current working directory.
1091  *
1092  * This has the consequence that in order to open the requestor with a
1093  * working directory and an empty filename, @filename must have a trailing
1094  * directory separator.
1095  *
1096  * The encoding of @filename is preferred GLib file name encoding, which
1097  * may not be UTF-8. See g_filename_from_utf8().
1098  **/
1099 void
gtk_file_selection_set_filename(GtkFileSelection * filesel,const gchar * filename)1100 gtk_file_selection_set_filename (GtkFileSelection *filesel,
1101 				 const gchar      *filename)
1102 {
1103   gchar *buf;
1104   const char *name, *last_slash;
1105   char *filename_utf8;
1106 
1107   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1108   g_return_if_fail (filename != NULL);
1109 
1110   filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1111   g_return_if_fail (filename_utf8 != NULL);
1112 
1113   last_slash = strrchr (filename_utf8, G_DIR_SEPARATOR);
1114 
1115   if (!last_slash)
1116     {
1117       buf = g_strdup ("");
1118       name = filename_utf8;
1119     }
1120   else
1121     {
1122       buf = g_strdup (filename_utf8);
1123       buf[last_slash - filename_utf8 + 1] = 0;
1124       name = last_slash + 1;
1125     }
1126 
1127   gtk_file_selection_populate (filesel, buf, FALSE, TRUE);
1128 
1129   if (filesel->selection_entry)
1130     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
1131   g_free (buf);
1132   g_object_notify (G_OBJECT (filesel), "filename");
1133 
1134   g_free (filename_utf8);
1135 }
1136 
1137 /**
1138  * gtk_file_selection_get_filename:
1139  * @filesel: a #GtkFileSelection
1140  *
1141  * This function returns the selected filename in the GLib file name
1142  * encoding. To convert to UTF-8, call g_filename_to_utf8(). The
1143  * returned string points to a statically allocated buffer and should
1144  * be copied if you plan to keep it around.
1145  *
1146  * If no file is selected then the selected directory path is returned.
1147  *
1148  * Return value: currently-selected filename in the on-disk encoding.
1149  **/
1150 const gchar*
gtk_file_selection_get_filename(GtkFileSelection * filesel)1151 gtk_file_selection_get_filename (GtkFileSelection *filesel)
1152 {
1153   static const gchar nothing[2] = "";
1154   static GString *something;
1155   char *sys_filename;
1156   const char *text;
1157 
1158   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
1159 
1160 #ifdef G_WITH_CYGWIN
1161   translate_win32_path (filesel);
1162 #endif
1163   text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
1164   if (text)
1165     {
1166       gchar *fullname = cmpl_completion_fullname (text, filesel->cmpl_state);
1167       sys_filename = g_filename_from_utf8 (fullname, -1, NULL, NULL, NULL);
1168       g_free (fullname);
1169       if (!sys_filename)
1170 	return nothing;
1171       if (!something)
1172         something = g_string_new (sys_filename);
1173       else
1174         g_string_assign (something, sys_filename);
1175       g_free (sys_filename);
1176 
1177       return something->str;
1178     }
1179 
1180   return nothing;
1181 }
1182 
1183 void
gtk_file_selection_complete(GtkFileSelection * filesel,const gchar * pattern)1184 gtk_file_selection_complete (GtkFileSelection *filesel,
1185 			     const gchar      *pattern)
1186 {
1187   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1188   g_return_if_fail (pattern != NULL);
1189 
1190   if (filesel->selection_entry)
1191     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
1192   gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE, TRUE);
1193 }
1194 
1195 static void
gtk_file_selection_destroy(GtkObject * object)1196 gtk_file_selection_destroy (GtkObject *object)
1197 {
1198   GtkFileSelection *filesel;
1199   GList *list;
1200   HistoryCallbackArg *callback_arg;
1201 
1202   g_return_if_fail (GTK_IS_FILE_SELECTION (object));
1203 
1204   filesel = GTK_FILE_SELECTION (object);
1205 
1206   if (filesel->fileop_dialog)
1207     {
1208       gtk_widget_destroy (filesel->fileop_dialog);
1209       filesel->fileop_dialog = NULL;
1210     }
1211 
1212   if (filesel->history_list)
1213     {
1214       list = filesel->history_list;
1215       while (list)
1216 	{
1217 	  callback_arg = list->data;
1218 	  g_free (callback_arg->directory);
1219 	  g_free (callback_arg);
1220 	  list = list->next;
1221 	}
1222       g_list_free (filesel->history_list);
1223       filesel->history_list = NULL;
1224     }
1225 
1226   if (filesel->cmpl_state)
1227     {
1228       cmpl_free_state (filesel->cmpl_state);
1229       filesel->cmpl_state = NULL;
1230     }
1231 
1232   if (filesel->selected_names)
1233     {
1234       free_selected_names (filesel->selected_names);
1235       filesel->selected_names = NULL;
1236     }
1237 
1238   if (filesel->last_selected)
1239     {
1240       g_free (filesel->last_selected);
1241       filesel->last_selected = NULL;
1242     }
1243 
1244   GTK_OBJECT_CLASS (gtk_file_selection_parent_class)->destroy (object);
1245 }
1246 
1247 static void
gtk_file_selection_map(GtkWidget * widget)1248 gtk_file_selection_map (GtkWidget *widget)
1249 {
1250   GtkFileSelection *filesel = GTK_FILE_SELECTION (widget);
1251 
1252   /* Refresh the contents */
1253   gtk_file_selection_populate (filesel, "", FALSE, FALSE);
1254 
1255   GTK_WIDGET_CLASS (gtk_file_selection_parent_class)->map (widget);
1256 }
1257 
1258 static void
gtk_file_selection_finalize(GObject * object)1259 gtk_file_selection_finalize (GObject *object)
1260 {
1261   GtkFileSelection *filesel = GTK_FILE_SELECTION (object);
1262 
1263   g_free (filesel->fileop_file);
1264 
1265   G_OBJECT_CLASS (gtk_file_selection_parent_class)->finalize (object);
1266 }
1267 
1268 /* Begin file operations callbacks */
1269 
1270 static void
gtk_file_selection_fileop_error(GtkFileSelection * fs,gchar * error_message)1271 gtk_file_selection_fileop_error (GtkFileSelection *fs,
1272 				 gchar            *error_message)
1273 {
1274   GtkWidget *dialog;
1275 
1276   g_return_if_fail (error_message != NULL);
1277 
1278   /* main dialog */
1279   dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
1280 				   GTK_DIALOG_DESTROY_WITH_PARENT,
1281 				   GTK_MESSAGE_ERROR,
1282 				   GTK_BUTTONS_OK,
1283 				   "%s", error_message);
1284 
1285   /* yes, we free it */
1286   g_free (error_message);
1287 
1288   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1289 
1290   g_signal_connect_swapped (dialog, "response",
1291 			    G_CALLBACK (gtk_widget_destroy),
1292 			    dialog);
1293 
1294   gtk_widget_show (dialog);
1295 }
1296 
1297 static void
gtk_file_selection_fileop_destroy(GtkWidget * widget,gpointer data)1298 gtk_file_selection_fileop_destroy (GtkWidget *widget,
1299 				   gpointer   data)
1300 {
1301   GtkFileSelection *fs = data;
1302 
1303   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1304 
1305   fs->fileop_dialog = NULL;
1306 }
1307 
1308 static gboolean
entry_is_empty(GtkEntry * entry)1309 entry_is_empty (GtkEntry *entry)
1310 {
1311   const gchar *text = gtk_entry_get_text (entry);
1312 
1313   return *text == '\0';
1314 }
1315 
1316 static void
gtk_file_selection_fileop_entry_changed(GtkEntry * entry,GtkWidget * button)1317 gtk_file_selection_fileop_entry_changed (GtkEntry   *entry,
1318 					 GtkWidget  *button)
1319 {
1320   gtk_widget_set_sensitive (button, !entry_is_empty (entry));
1321 }
1322 
1323 static void
gtk_file_selection_create_dir_confirmed(GtkWidget * widget,gpointer data)1324 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
1325 					 gpointer   data)
1326 {
1327   GtkFileSelection *fs = data;
1328   const gchar *dirname;
1329   gchar *path;
1330   gchar *full_path;
1331   gchar *sys_full_path;
1332   gchar *buf;
1333   GError *error = NULL;
1334   CompletionState *cmpl_state;
1335 
1336   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1337 
1338   dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1339   cmpl_state = (CompletionState*) fs->cmpl_state;
1340   path = cmpl_reference_position (cmpl_state);
1341 
1342   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
1343   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1344   if (error)
1345     {
1346       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1347 	buf = g_strdup_printf (_("The folder name \"%s\" contains symbols that are not allowed in filenames"), dirname);
1348       else
1349 	buf = g_strdup_printf (_("Error creating folder '%s': %s"),
1350 			       dirname, error->message);
1351       gtk_file_selection_fileop_error (fs, buf);
1352       g_error_free (error);
1353       goto out;
1354     }
1355 
1356   if (g_mkdir (sys_full_path, 0777) < 0)
1357     {
1358       int errsv = errno;
1359 
1360       buf = g_strdup_printf (_("Error creating folder '%s': %s"),
1361 			     dirname, g_strerror (errsv));
1362       gtk_file_selection_fileop_error (fs, buf);
1363     }
1364 
1365  out:
1366   g_free (full_path);
1367   g_free (sys_full_path);
1368 
1369   gtk_widget_destroy (fs->fileop_dialog);
1370   gtk_file_selection_populate (fs, "", FALSE, FALSE);
1371 }
1372 
1373 static void
gtk_file_selection_create_dir(GtkWidget * widget,gpointer data)1374 gtk_file_selection_create_dir (GtkWidget *widget,
1375 			       gpointer   data)
1376 {
1377   GtkFileSelection *fs = data;
1378   GtkWidget *label;
1379   GtkWidget *dialog;
1380   GtkWidget *vbox;
1381   GtkWidget *button;
1382 
1383   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1384 
1385   if (fs->fileop_dialog)
1386     return;
1387 
1388   /* main dialog */
1389   dialog = gtk_dialog_new ();
1390   fs->fileop_dialog = dialog;
1391   g_signal_connect (dialog, "destroy",
1392 		    G_CALLBACK (gtk_file_selection_fileop_destroy),
1393 		    fs);
1394   gtk_window_set_title (GTK_WINDOW (dialog), _("New Folder"));
1395   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1396   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1397 
1398   /* If file dialog is grabbed, grab option dialog */
1399   /* When option dialog is closed, file dialog will be grabbed again */
1400   if (GTK_WINDOW (fs)->modal)
1401       gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1402 
1403   vbox = gtk_vbox_new (FALSE, 0);
1404   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1405   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1406 		     FALSE, FALSE, 0);
1407   gtk_widget_show( vbox);
1408 
1409   label = gtk_label_new_with_mnemonic (_("_Folder name:"));
1410   gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
1411   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1412   gtk_widget_show (label);
1413 
1414   /*  The directory entry widget  */
1415   fs->fileop_entry = gtk_entry_new ();
1416   gtk_label_set_mnemonic_widget (GTK_LABEL (label), fs->fileop_entry);
1417   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
1418 		      TRUE, TRUE, 5);
1419   gtk_widget_set_can_default (fs->fileop_entry, TRUE);
1420   gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE);
1421   gtk_widget_show (fs->fileop_entry);
1422 
1423   /* buttons */
1424   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1425 				  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1426   g_signal_connect_swapped (button, "clicked",
1427 			    G_CALLBACK (gtk_widget_destroy),
1428 			    dialog);
1429 
1430   gtk_widget_grab_focus (fs->fileop_entry);
1431 
1432   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1433 				  _("C_reate"), GTK_RESPONSE_OK);
1434   gtk_widget_set_sensitive (button, FALSE);
1435   g_signal_connect (button, "clicked",
1436 		    G_CALLBACK (gtk_file_selection_create_dir_confirmed),
1437 		    fs);
1438   g_signal_connect (fs->fileop_entry, "changed",
1439                     G_CALLBACK (gtk_file_selection_fileop_entry_changed),
1440 		    button);
1441 
1442   gtk_widget_grab_default (button);
1443 
1444   gtk_widget_show (dialog);
1445 }
1446 
1447 static void
gtk_file_selection_delete_file_response(GtkDialog * dialog,gint response_id,gpointer data)1448 gtk_file_selection_delete_file_response (GtkDialog *dialog,
1449                                          gint       response_id,
1450                                          gpointer   data)
1451 {
1452   GtkFileSelection *fs = data;
1453   CompletionState *cmpl_state;
1454   gchar *path;
1455   gchar *full_path;
1456   gchar *sys_full_path;
1457   GError *error = NULL;
1458   gchar *buf;
1459 
1460   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1461 
1462   if (response_id != GTK_RESPONSE_OK)
1463     {
1464       gtk_widget_destroy (GTK_WIDGET (dialog));
1465       return;
1466     }
1467 
1468   cmpl_state = (CompletionState*) fs->cmpl_state;
1469   path = cmpl_reference_position (cmpl_state);
1470 
1471   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1472   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1473   if (error)
1474     {
1475       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1476 	buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1477 			       fs->fileop_file);
1478       else
1479 	buf = g_strdup_printf (_("Error deleting file '%s': %s"),
1480 			       fs->fileop_file, error->message);
1481 
1482       gtk_file_selection_fileop_error (fs, buf);
1483       g_error_free (error);
1484       goto out;
1485     }
1486 
1487   if (g_unlink (sys_full_path) < 0)
1488     {
1489       int errsv = errno;
1490 
1491       buf = g_strdup_printf (_("Error deleting file '%s': %s"),
1492 			     fs->fileop_file, g_strerror (errsv));
1493       gtk_file_selection_fileop_error (fs, buf);
1494     }
1495 
1496  out:
1497   g_free (full_path);
1498   g_free (sys_full_path);
1499 
1500   gtk_widget_destroy (fs->fileop_dialog);
1501   gtk_file_selection_populate (fs, "", FALSE, TRUE);
1502 }
1503 
1504 static void
gtk_file_selection_delete_file(GtkWidget * widget,gpointer data)1505 gtk_file_selection_delete_file (GtkWidget *widget,
1506 				gpointer   data)
1507 {
1508   GtkFileSelection *fs = data;
1509   GtkWidget *dialog;
1510   const gchar *filename;
1511 
1512   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1513 
1514   if (fs->fileop_dialog)
1515     return;
1516 
1517 #ifdef G_WITH_CYGWIN
1518   translate_win32_path (fs);
1519 #endif
1520 
1521   filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1522   if (strlen (filename) < 1)
1523     return;
1524 
1525   g_free (fs->fileop_file);
1526   fs->fileop_file = g_strdup (filename);
1527 
1528   /* main dialog */
1529   fs->fileop_dialog = dialog =
1530     gtk_message_dialog_new (GTK_WINDOW (fs),
1531                             GTK_WINDOW (fs)->modal ? GTK_DIALOG_MODAL : 0,
1532                             GTK_MESSAGE_QUESTION,
1533                             GTK_BUTTONS_NONE,
1534                             _("Really delete file \"%s\"?"), filename);
1535 
1536   g_signal_connect (dialog, "destroy",
1537 		    G_CALLBACK (gtk_file_selection_fileop_destroy),
1538 		    fs);
1539   gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1540   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1541 
1542   /* buttons */
1543   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1544                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1545                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
1546                           NULL);
1547 
1548   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
1549 
1550   g_signal_connect (dialog, "response",
1551                     G_CALLBACK (gtk_file_selection_delete_file_response),
1552                     fs);
1553 
1554   gtk_widget_show (dialog);
1555 }
1556 
1557 static void
gtk_file_selection_rename_file_confirmed(GtkWidget * widget,gpointer data)1558 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1559 					  gpointer   data)
1560 {
1561   GtkFileSelection *fs = data;
1562   gchar *buf;
1563   const gchar *file;
1564   gchar *path;
1565   gchar *new_filename;
1566   gchar *old_filename;
1567   gchar *sys_new_filename;
1568   gchar *sys_old_filename;
1569   CompletionState *cmpl_state;
1570   GError *error = NULL;
1571 
1572   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1573 
1574   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1575   cmpl_state = (CompletionState*) fs->cmpl_state;
1576   path = cmpl_reference_position (cmpl_state);
1577 
1578   new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1579   old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1580 
1581   sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
1582   if (error)
1583     {
1584       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1585 	buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"), new_filename);
1586       else
1587 	buf = g_strdup_printf (_("Error renaming file to \"%s\": %s"),
1588 			       new_filename, error->message);
1589       gtk_file_selection_fileop_error (fs, buf);
1590       g_error_free (error);
1591       goto out1;
1592     }
1593 
1594   sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
1595   if (error)
1596     {
1597       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1598 	buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"), old_filename);
1599       else
1600 	buf = g_strdup_printf (_("Error renaming file \"%s\": %s"),
1601 			       old_filename, error->message);
1602       gtk_file_selection_fileop_error (fs, buf);
1603       g_error_free (error);
1604       goto out2;
1605     }
1606 
1607   if (g_rename (sys_old_filename, sys_new_filename) < 0)
1608     {
1609       int errsv = errno;
1610 
1611       buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1612 			     sys_old_filename, sys_new_filename,
1613 			     g_strerror (errsv));
1614       gtk_file_selection_fileop_error (fs, buf);
1615       goto out2;
1616     }
1617 
1618   gtk_file_selection_populate (fs, "", FALSE, FALSE);
1619   gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), file);
1620 
1621  out2:
1622   g_free (sys_old_filename);
1623 
1624  out1:
1625   g_free (new_filename);
1626   g_free (old_filename);
1627   g_free (sys_new_filename);
1628 
1629   gtk_widget_destroy (fs->fileop_dialog);
1630 }
1631 
1632 static void
gtk_file_selection_rename_file(GtkWidget * widget,gpointer data)1633 gtk_file_selection_rename_file (GtkWidget *widget,
1634 				gpointer   data)
1635 {
1636   GtkFileSelection *fs = data;
1637   GtkWidget *label;
1638   GtkWidget *dialog;
1639   GtkWidget *vbox;
1640   GtkWidget *button;
1641   gchar *buf;
1642 
1643   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1644 
1645   if (fs->fileop_dialog)
1646 	  return;
1647 
1648   g_free (fs->fileop_file);
1649   fs->fileop_file = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1650   if (strlen (fs->fileop_file) < 1)
1651     return;
1652 
1653   /* main dialog */
1654   fs->fileop_dialog = dialog = gtk_dialog_new ();
1655   g_signal_connect (dialog, "destroy",
1656 		    G_CALLBACK (gtk_file_selection_fileop_destroy),
1657 		    fs);
1658   gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1659   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1660   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1661 
1662   /* If file dialog is grabbed, grab option dialog */
1663   /* When option dialog  closed, file dialog will be grabbed again */
1664   if (GTK_WINDOW (fs)->modal)
1665     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1666 
1667   vbox = gtk_vbox_new (FALSE, 0);
1668   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1669   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1670 		      FALSE, FALSE, 0);
1671   gtk_widget_show(vbox);
1672 
1673   buf = g_strdup_printf (_("Rename file \"%s\" to:"), fs->fileop_file);
1674   label = gtk_label_new (buf);
1675   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1676   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1677   gtk_widget_show (label);
1678   g_free (buf);
1679 
1680   /* New filename entry */
1681   fs->fileop_entry = gtk_entry_new ();
1682   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
1683 		      TRUE, TRUE, 5);
1684   gtk_widget_set_can_default (fs->fileop_entry, TRUE);
1685   gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE);
1686   gtk_widget_show (fs->fileop_entry);
1687 
1688   gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1689   gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1690 			      0, strlen (fs->fileop_file));
1691 
1692   /* buttons */
1693   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1694 				  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1695   g_signal_connect_swapped (button, "clicked",
1696 			    G_CALLBACK (gtk_widget_destroy),
1697 			    dialog);
1698 
1699   gtk_widget_grab_focus (fs->fileop_entry);
1700 
1701   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1702 				  _("_Rename"), GTK_RESPONSE_OK);
1703   g_signal_connect (button, "clicked",
1704 		    G_CALLBACK (gtk_file_selection_rename_file_confirmed),
1705 		    fs);
1706   g_signal_connect (fs->fileop_entry, "changed",
1707 		    G_CALLBACK (gtk_file_selection_fileop_entry_changed),
1708 		    button);
1709 
1710   gtk_widget_grab_default (button);
1711 
1712   gtk_widget_show (dialog);
1713 }
1714 
1715 static gint
gtk_file_selection_insert_text(GtkWidget * widget,const gchar * new_text,gint new_text_length,gint * position,gpointer user_data)1716 gtk_file_selection_insert_text (GtkWidget   *widget,
1717 				const gchar *new_text,
1718 				gint         new_text_length,
1719 				gint        *position,
1720 				gpointer     user_data)
1721 {
1722   gchar *filename;
1723 
1724   filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
1725 
1726   if (!filename)
1727     {
1728       gdk_display_beep (gtk_widget_get_display (widget));
1729       g_signal_stop_emission_by_name (widget, "insert-text");
1730       return FALSE;
1731     }
1732 
1733   g_free (filename);
1734 
1735   return TRUE;
1736 }
1737 
1738 static void
gtk_file_selection_update_fileops(GtkFileSelection * fs)1739 gtk_file_selection_update_fileops (GtkFileSelection *fs)
1740 {
1741   gboolean sensitive;
1742 
1743   if (!fs->selection_entry)
1744     return;
1745 
1746   sensitive = !entry_is_empty (GTK_ENTRY (fs->selection_entry));
1747 
1748   if (fs->fileop_del_file)
1749     gtk_widget_set_sensitive (fs->fileop_del_file, sensitive);
1750 
1751   if (fs->fileop_ren_file)
1752     gtk_widget_set_sensitive (fs->fileop_ren_file, sensitive);
1753 }
1754 
1755 static gint
gtk_file_selection_key_press(GtkWidget * widget,GdkEventKey * event,gpointer user_data)1756 gtk_file_selection_key_press (GtkWidget   *widget,
1757 			      GdkEventKey *event,
1758 			      gpointer     user_data)
1759 {
1760   GtkFileSelection *fs;
1761   char *text;
1762 
1763   g_return_val_if_fail (widget != NULL, FALSE);
1764   g_return_val_if_fail (event != NULL, FALSE);
1765 
1766   if ((event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab) &&
1767       (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
1768     {
1769       fs = GTK_FILE_SELECTION (user_data);
1770 #ifdef G_WITH_CYGWIN
1771       translate_win32_path (fs);
1772 #endif
1773       text = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1774 
1775       gtk_file_selection_populate (fs, text, TRUE, TRUE);
1776 
1777       g_free (text);
1778 
1779       return TRUE;
1780     }
1781 
1782   return FALSE;
1783 }
1784 
1785 static void
gtk_file_selection_history_callback(GtkWidget * widget,gpointer data)1786 gtk_file_selection_history_callback (GtkWidget *widget,
1787 				     gpointer   data)
1788 {
1789   GtkFileSelection *fs = data;
1790   HistoryCallbackArg *callback_arg;
1791   GList *list;
1792 
1793   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1794 
1795   list = fs->history_list;
1796 
1797   while (list) {
1798     callback_arg = list->data;
1799 
1800     if (callback_arg->menu_item == widget)
1801       {
1802 	gtk_file_selection_populate (fs, callback_arg->directory, FALSE, FALSE);
1803 	break;
1804       }
1805 
1806     list = list->next;
1807   }
1808 }
1809 
1810 static void
gtk_file_selection_update_history_menu(GtkFileSelection * fs,gchar * current_directory)1811 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1812 					gchar            *current_directory)
1813 {
1814   HistoryCallbackArg *callback_arg;
1815   GtkWidget *menu_item;
1816   GList *list;
1817   gchar *current_dir;
1818   gint dir_len;
1819   gint i;
1820 
1821   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1822   g_return_if_fail (current_directory != NULL);
1823 
1824   list = fs->history_list;
1825 
1826   if (fs->history_menu)
1827     {
1828       while (list) {
1829 	callback_arg = list->data;
1830 	g_free (callback_arg->directory);
1831 	g_free (callback_arg);
1832 	list = list->next;
1833       }
1834       g_list_free (fs->history_list);
1835       fs->history_list = NULL;
1836 
1837       gtk_widget_destroy (fs->history_menu);
1838     }
1839 
1840   fs->history_menu = gtk_menu_new ();
1841 
1842   current_dir = g_strdup (current_directory);
1843 
1844   dir_len = strlen (current_dir);
1845 
1846   for (i = dir_len; i >= 0; i--)
1847     {
1848       /* the i == dir_len is to catch the full path for the first
1849        * entry. */
1850       if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1851 	{
1852 	  /* another small hack to catch the full path */
1853 	  if (i != dir_len)
1854 		  current_dir[i + 1] = '\0';
1855 #ifdef G_WITH_CYGWIN
1856 	  if (!strcmp (current_dir, "//"))
1857 	    continue;
1858 #endif
1859 	  menu_item = gtk_menu_item_new_with_label (current_dir);
1860 
1861 	  callback_arg = g_new (HistoryCallbackArg, 1);
1862 	  callback_arg->menu_item = menu_item;
1863 
1864 	  /* since the autocompletion gets confused if you don't
1865 	   * supply a trailing '/' on a dir entry, set the full
1866 	   * (current) path to "" which just refreshes the filesel */
1867 	  if (dir_len == i)
1868 	    {
1869 	      callback_arg->directory = g_strdup ("");
1870 	    }
1871 	  else
1872 	    {
1873 	      callback_arg->directory = g_strdup (current_dir);
1874 	    }
1875 
1876 	  fs->history_list = g_list_append (fs->history_list, callback_arg);
1877 
1878 	  g_signal_connect (menu_item, "activate",
1879 			    G_CALLBACK (gtk_file_selection_history_callback),
1880 			    fs);
1881 	  gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1882 	  gtk_widget_show (menu_item);
1883 	}
1884     }
1885 
1886   gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown),
1887 			    fs->history_menu);
1888   g_free (current_dir);
1889 }
1890 
1891 static gchar *
get_real_filename(gchar * filename,gboolean free_old)1892 get_real_filename (gchar    *filename,
1893                    gboolean  free_old)
1894 {
1895 #ifdef G_WITH_CYGWIN
1896   /* Check to see if the selection was a drive selector */
1897   if (isalpha (filename[0]) && (filename[1] == ':'))
1898     {
1899       gchar temp_filename[MAX_PATH];
1900       int len;
1901 
1902       cygwin_conv_to_posix_path (filename, temp_filename);
1903 
1904       /* we need trailing '/'. */
1905       len = strlen (temp_filename);
1906       if (len > 0 && temp_filename[len-1] != '/')
1907         {
1908           temp_filename[len]   = '/';
1909           temp_filename[len+1] = '\0';
1910         }
1911 
1912       if (free_old)
1913 	g_free (filename);
1914 
1915       return g_strdup (temp_filename);
1916     }
1917 #endif /* G_WITH_CYGWIN */
1918   return filename;
1919 }
1920 
1921 static void
gtk_file_selection_file_activate(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gpointer user_data)1922 gtk_file_selection_file_activate (GtkTreeView       *tree_view,
1923 				  GtkTreePath       *path,
1924 				  GtkTreeViewColumn *column,
1925 				  gpointer           user_data)
1926 {
1927   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1928   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1929   GtkTreeIter iter;
1930   gchar *filename;
1931 
1932   gtk_tree_model_get_iter (model, &iter, path);
1933   gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1);
1934   filename = get_real_filename (filename, TRUE);
1935   gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1936   gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1937 
1938   g_free (filename);
1939 }
1940 
1941 static void
gtk_file_selection_dir_activate(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gpointer user_data)1942 gtk_file_selection_dir_activate (GtkTreeView       *tree_view,
1943 				 GtkTreePath       *path,
1944 				 GtkTreeViewColumn *column,
1945 				 gpointer           user_data)
1946 {
1947   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1948   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1949   GtkTreeIter iter;
1950   gchar *filename;
1951 
1952   gtk_tree_model_get_iter (model, &iter, path);
1953   gtk_tree_model_get (model, &iter, DIR_COLUMN, &filename, -1);
1954   filename = get_real_filename (filename, TRUE);
1955   gtk_file_selection_populate (fs, filename, FALSE, FALSE);
1956   g_free (filename);
1957 }
1958 
1959 #ifdef G_PLATFORM_WIN32
1960 
1961 static void
win32_gtk_add_drives_to_dir_list(GtkListStore * model)1962 win32_gtk_add_drives_to_dir_list (GtkListStore *model)
1963 {
1964   gchar *textPtr;
1965   gchar buffer[128];
1966   char formatBuffer[128];
1967   GtkTreeIter iter;
1968 
1969   /* Get the drives string */
1970   GetLogicalDriveStrings (sizeof (buffer), buffer);
1971 
1972   /* Add the drives as necessary */
1973   textPtr = buffer;
1974   while (*textPtr != '\0')
1975     {
1976       /* Ignore floppies (?) */
1977       if (GetDriveType (textPtr) != DRIVE_REMOVABLE)
1978 	{
1979 	  /* Build the actual displayable string */
1980 	  g_snprintf (formatBuffer, sizeof (formatBuffer), "%c:\\", toupper (textPtr[0]));
1981 
1982 	  /* Add to the list */
1983 	  gtk_list_store_append (model, &iter);
1984 	  gtk_list_store_set (model, &iter, DIR_COLUMN, formatBuffer, -1);
1985 	}
1986       textPtr += (strlen (textPtr) + 1);
1987     }
1988 }
1989 #endif
1990 
1991 static gchar *
escape_underscores(const gchar * str)1992 escape_underscores (const gchar *str)
1993 {
1994   GString *result = g_string_new (NULL);
1995   while (*str)
1996     {
1997       if (*str == '_')
1998 	g_string_append_c (result, '_');
1999 
2000       g_string_append_c (result, *str);
2001       str++;
2002     }
2003 
2004   return g_string_free (result, FALSE);
2005 }
2006 
2007 static void
gtk_file_selection_populate(GtkFileSelection * fs,gchar * rel_path,gboolean try_complete,gboolean reset_entry)2008 gtk_file_selection_populate (GtkFileSelection *fs,
2009 			     gchar            *rel_path,
2010 			     gboolean          try_complete,
2011 			     gboolean          reset_entry)
2012 {
2013   CompletionState *cmpl_state;
2014   PossibleCompletion* poss;
2015   GtkTreeIter iter;
2016   GtkListStore *dir_model;
2017   GtkListStore *file_model;
2018   gchar* filename;
2019   gchar* rem_path = rel_path;
2020   gchar* sel_text;
2021   gint did_recurse = FALSE;
2022   gint possible_count = 0;
2023 
2024   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2025 
2026   cmpl_state = (CompletionState*) fs->cmpl_state;
2027   poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
2028 
2029   if (!cmpl_state_okay (cmpl_state))
2030     {
2031       /* Something went wrong. */
2032       gtk_file_selection_abort (fs);
2033       return;
2034     }
2035 
2036   g_assert (cmpl_state->reference_dir);
2037 
2038   dir_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->dir_list)));
2039   file_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->file_list)));
2040 
2041   gtk_list_store_clear (dir_model);
2042   gtk_list_store_clear (file_model);
2043 
2044   /* Set the dir list to include ./ and ../ */
2045   gtk_list_store_append (dir_model, &iter);
2046   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, "." G_DIR_SEPARATOR_S, -1);
2047   gtk_list_store_append (dir_model, &iter);
2048   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, ".." G_DIR_SEPARATOR_S, -1);
2049 
2050   while (poss)
2051     {
2052       if (cmpl_is_a_completion (poss))
2053         {
2054           possible_count += 1;
2055 
2056           filename = cmpl_this_completion (poss);
2057 
2058           if (cmpl_is_directory (poss))
2059             {
2060               if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
2061                   strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
2062 		{
2063 		  gtk_list_store_append (dir_model, &iter);
2064 		  gtk_list_store_set (dir_model, &iter, DIR_COLUMN, filename, -1);
2065 		}
2066 	    }
2067           else
2068 	    {
2069 	      gtk_list_store_append (file_model, &iter);
2070 	      gtk_list_store_set (file_model, &iter, DIR_COLUMN, filename, -1);
2071             }
2072 	}
2073 
2074       poss = cmpl_next_completion (cmpl_state);
2075     }
2076 
2077 #ifdef G_PLATFORM_WIN32
2078   /* For Windows, add drives as potential selections */
2079   win32_gtk_add_drives_to_dir_list (dir_model);
2080 #endif
2081 
2082   /* File lists are set. */
2083 
2084   g_assert (cmpl_state->reference_dir);
2085 
2086   if (try_complete)
2087     {
2088 
2089       /* User is trying to complete filenames, so advance the user's input
2090        * string to the updated_text, which is the common leading substring
2091        * of all possible completions, and if its a directory attempt
2092        * attempt completions in it. */
2093 
2094       if (cmpl_updated_text (cmpl_state)[0])
2095         {
2096 
2097           if (cmpl_updated_dir (cmpl_state))
2098             {
2099 	      gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
2100 
2101               did_recurse = TRUE;
2102 
2103               gtk_file_selection_populate (fs, dir_name, TRUE, TRUE);
2104 
2105               g_free (dir_name);
2106             }
2107           else
2108             {
2109 	      if (fs->selection_entry)
2110 		      gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
2111 					  cmpl_updated_text (cmpl_state));
2112             }
2113         }
2114       else
2115         {
2116 	  if (fs->selection_entry)
2117 	    gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
2118         }
2119     }
2120   else if (reset_entry)
2121     {
2122       if (fs->selection_entry)
2123 	gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2124     }
2125 
2126   if (!did_recurse)
2127     {
2128       if (fs->selection_entry && try_complete)
2129 	gtk_editable_set_position (GTK_EDITABLE (fs->selection_entry), -1);
2130 
2131       if (fs->selection_entry)
2132 	{
2133 	  char *escaped = escape_underscores (cmpl_reference_position (cmpl_state));
2134 	  sel_text = g_strconcat (_("_Selection: "), escaped, NULL);
2135 	  g_free (escaped);
2136 
2137 	  gtk_label_set_text_with_mnemonic (GTK_LABEL (fs->selection_text), sel_text);
2138 	  g_free (sel_text);
2139 	}
2140 
2141       if (fs->history_pulldown)
2142 	{
2143 	  gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
2144 	}
2145 
2146     }
2147 }
2148 
2149 static void
gtk_file_selection_abort(GtkFileSelection * fs)2150 gtk_file_selection_abort (GtkFileSelection *fs)
2151 {
2152   gchar err_buf[256];
2153 
2154   g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
2155 
2156   /*  BEEP gdk_beep();  */
2157 
2158   if (fs->selection_entry)
2159     gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
2160 }
2161 
2162 /**
2163  * gtk_file_selection_set_select_multiple:
2164  * @filesel: a #GtkFileSelection
2165  * @select_multiple: whether or not the user is allowed to select multiple
2166  * files in the file list.
2167  *
2168  * Sets whether the user is allowed to select multiple files in the file list.
2169  * Use gtk_file_selection_get_selections () to get the list of selected files.
2170  **/
2171 void
gtk_file_selection_set_select_multiple(GtkFileSelection * filesel,gboolean select_multiple)2172 gtk_file_selection_set_select_multiple (GtkFileSelection *filesel,
2173 					gboolean          select_multiple)
2174 {
2175   GtkTreeSelection *sel;
2176   GtkSelectionMode mode;
2177 
2178   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
2179 
2180   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2181 
2182   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
2183 
2184   if (mode != gtk_tree_selection_get_mode (sel))
2185     {
2186       gtk_tree_selection_set_mode (sel, mode);
2187 
2188       g_object_notify (G_OBJECT (filesel), "select-multiple");
2189     }
2190 }
2191 
2192 /**
2193  * gtk_file_selection_get_select_multiple:
2194  * @filesel: a #GtkFileSelection
2195  *
2196  * Determines whether or not the user is allowed to select multiple files in
2197  * the file list. See gtk_file_selection_set_select_multiple().
2198  *
2199  * Return value: %TRUE if the user is allowed to select multiple files in the
2200  * file list
2201  **/
2202 gboolean
gtk_file_selection_get_select_multiple(GtkFileSelection * filesel)2203 gtk_file_selection_get_select_multiple (GtkFileSelection *filesel)
2204 {
2205   GtkTreeSelection *sel;
2206 
2207   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), FALSE);
2208 
2209   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2210   return (gtk_tree_selection_get_mode (sel) == GTK_SELECTION_MULTIPLE);
2211 }
2212 
2213 static void
multiple_changed_foreach(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)2214 multiple_changed_foreach (GtkTreeModel *model,
2215 			  GtkTreePath  *path,
2216 			  GtkTreeIter  *iter,
2217 			  gpointer      data)
2218 {
2219   GPtrArray *names = data;
2220   gchar *filename;
2221 
2222   gtk_tree_model_get (model, iter, FILE_COLUMN, &filename, -1);
2223 
2224   g_ptr_array_add (names, filename);
2225 }
2226 
2227 static void
free_selected_names(GPtrArray * names)2228 free_selected_names (GPtrArray *names)
2229 {
2230   gint i;
2231 
2232   for (i = 0; i < names->len; i++)
2233     g_free (g_ptr_array_index (names, i));
2234 
2235   g_ptr_array_free (names, TRUE);
2236 }
2237 
2238 static void
gtk_file_selection_file_changed(GtkTreeSelection * selection,gpointer user_data)2239 gtk_file_selection_file_changed (GtkTreeSelection *selection,
2240 				 gpointer          user_data)
2241 {
2242   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2243   GPtrArray *new_names;
2244   gchar *filename;
2245   const gchar *entry;
2246   gint index = -1;
2247 
2248   new_names = g_ptr_array_sized_new (8);
2249 
2250   gtk_tree_selection_selected_foreach (selection,
2251 				       multiple_changed_foreach,
2252 				       new_names);
2253 
2254   /* nothing selected */
2255   if (new_names->len == 0)
2256     {
2257       g_ptr_array_free (new_names, TRUE);
2258 
2259       if (fs->selected_names != NULL)
2260 	{
2261 	  free_selected_names (fs->selected_names);
2262 	  fs->selected_names = NULL;
2263 	}
2264 
2265       goto maybe_clear_entry;
2266     }
2267 
2268   if (new_names->len != 1)
2269     {
2270       GPtrArray *old_names = fs->selected_names;
2271 
2272       if (old_names != NULL)
2273 	{
2274 	  /* A common case is selecting a range of files from top to bottom,
2275 	   * so quickly check for that to avoid looping over the entire list
2276 	   */
2277 	  if (compare_utf8_filenames (g_ptr_array_index (old_names, old_names->len - 1),
2278 				      g_ptr_array_index (new_names, new_names->len - 1)) != 0)
2279 	    index = new_names->len - 1;
2280 	  else
2281 	    {
2282 	      gint i = 0, j = 0, cmp;
2283 
2284 	      /* do a quick diff, stopping at the first file not in the
2285 	       * old list
2286 	       */
2287 	      while (i < old_names->len && j < new_names->len)
2288 		{
2289 		  cmp = compare_utf8_filenames (g_ptr_array_index (old_names, i),
2290 						g_ptr_array_index (new_names, j));
2291 		  if (cmp < 0)
2292 		    {
2293 		      i++;
2294 		    }
2295 		  else if (cmp == 0)
2296 		    {
2297 		      i++;
2298 		      j++;
2299 		    }
2300 		  else if (cmp > 0)
2301 		    {
2302 		      index = j;
2303 		      break;
2304 		    }
2305 		}
2306 
2307 	      /* we ran off the end of the old list */
2308 	      if (index == -1 && i < new_names->len)
2309 		index = j;
2310 	    }
2311 	}
2312       else
2313 	{
2314 	  /* A phantom anchor still exists at the point where the last item
2315 	   * was selected, which is used for subsequent range selections.
2316 	   * So search up from there.
2317 	   */
2318 	  if (fs->last_selected &&
2319 	      compare_utf8_filenames (fs->last_selected,
2320 				      g_ptr_array_index (new_names, 0)) == 0)
2321 	    index = new_names->len - 1;
2322 	  else
2323 	    index = 0;
2324 	}
2325     }
2326   else
2327     index = 0;
2328 
2329   if (fs->selected_names != NULL)
2330     free_selected_names (fs->selected_names);
2331 
2332   fs->selected_names = new_names;
2333 
2334   if (index != -1)
2335     {
2336       g_free (fs->last_selected);
2337 
2338       fs->last_selected = g_strdup (g_ptr_array_index (new_names, index));
2339       filename = get_real_filename (fs->last_selected, FALSE);
2340 
2341       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
2342 
2343       if (filename != fs->last_selected)
2344 	g_free (filename);
2345 
2346       return;
2347     }
2348 
2349 maybe_clear_entry:
2350 
2351   entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2352   if ((entry != NULL) && (fs->last_selected != NULL) &&
2353       (compare_utf8_filenames (entry, fs->last_selected) == 0))
2354     gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2355 }
2356 
2357 /**
2358  * gtk_file_selection_get_selections:
2359  * @filesel: a #GtkFileSelection
2360  *
2361  * Retrieves the list of file selections the user has made in the dialog box.
2362  * This function is intended for use when the user can select multiple files
2363  * in the file list.
2364  *
2365  * The filenames are in the GLib file name encoding. To convert to
2366  * UTF-8, call g_filename_to_utf8() on each string.
2367  *
2368  * Return value: a newly-allocated %NULL-terminated array of strings. Use
2369  * g_strfreev() to free it.
2370  **/
2371 gchar **
gtk_file_selection_get_selections(GtkFileSelection * filesel)2372 gtk_file_selection_get_selections (GtkFileSelection *filesel)
2373 {
2374   GPtrArray *names;
2375   gchar **selections;
2376   gchar *filename, *dirname;
2377   gchar *current, *buf;
2378   gint i, count;
2379   gboolean unselected_entry;
2380 
2381   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL);
2382 
2383   filename = g_strdup (gtk_file_selection_get_filename (filesel));
2384 
2385   if (strlen (filename) == 0)
2386     {
2387       g_free (filename);
2388       return NULL;
2389     }
2390 
2391   names = filesel->selected_names;
2392 
2393   if (names != NULL)
2394     selections = g_new (gchar *, names->len + 2);
2395   else
2396     selections = g_new (gchar *, 2);
2397 
2398   count = 0;
2399   unselected_entry = TRUE;
2400 
2401   if (names != NULL)
2402     {
2403       dirname = g_path_get_dirname (filename);
2404 
2405       if ((names->len >= 1) &&
2406 	  (strcmp (gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry)), "") == 0))
2407 	{ /* multiple files are selected and last selection was removed via ctrl click */
2408 	  g_free (dirname);
2409 	  dirname = g_strdup (filename); /* as gtk_file_selection_get_filename returns dir
2410 					    if no file is selected */
2411 	  unselected_entry = FALSE;
2412 	}
2413 
2414       for (i = 0; i < names->len; i++)
2415 	{
2416 	  buf = g_filename_from_utf8 (g_ptr_array_index (names, i), -1,
2417 				      NULL, NULL, NULL);
2418 	  current = g_build_filename (dirname, buf, NULL);
2419 	  g_free (buf);
2420 
2421 	  selections[count++] = current;
2422 
2423 	  if (unselected_entry && compare_sys_filenames (current, filename) == 0)
2424 	    unselected_entry = FALSE;
2425 	}
2426 
2427       g_free (dirname);
2428     }
2429 
2430   if (unselected_entry)
2431     selections[count++] = filename;
2432   else
2433     g_free (filename);
2434 
2435   selections[count] = NULL;
2436 
2437   return selections;
2438 }
2439 
2440 /**********************************************************************/
2441 /*			  External Interface                          */
2442 /**********************************************************************/
2443 
2444 /* The four completion state selectors
2445  */
2446 static gchar*
cmpl_updated_text(CompletionState * cmpl_state)2447 cmpl_updated_text (CompletionState *cmpl_state)
2448 {
2449   return cmpl_state->updated_text;
2450 }
2451 
2452 static gboolean
cmpl_updated_dir(CompletionState * cmpl_state)2453 cmpl_updated_dir (CompletionState *cmpl_state)
2454 {
2455   return cmpl_state->re_complete;
2456 }
2457 
2458 static gchar*
cmpl_reference_position(CompletionState * cmpl_state)2459 cmpl_reference_position (CompletionState *cmpl_state)
2460 {
2461   return cmpl_state->reference_dir->fullname;
2462 }
2463 
2464 #if 0
2465 /* This doesn't work currently and would require changes
2466  * to fnmatch.c to get working.
2467  */
2468 static gint
2469 cmpl_last_valid_char (CompletionState *cmpl_state)
2470 {
2471   return cmpl_state->last_valid_char;
2472 }
2473 #endif
2474 
2475 static gchar*
cmpl_completion_fullname(const gchar * text,CompletionState * cmpl_state)2476 cmpl_completion_fullname (const gchar     *text,
2477 			  CompletionState *cmpl_state)
2478 {
2479   if (!cmpl_state_okay (cmpl_state))
2480     {
2481       return g_strdup ("");
2482     }
2483   else if (g_path_is_absolute (text))
2484     {
2485       return g_strdup (text);
2486     }
2487 #ifdef HAVE_PWD_H
2488   else if (text[0] == '~')
2489     {
2490       CompletionDir* dir;
2491       char* slash;
2492 
2493       dir = open_user_dir (text, cmpl_state);
2494 
2495       if (dir)
2496 	{
2497 	  slash = strchr (text, G_DIR_SEPARATOR);
2498 
2499 	  /* slash may be NULL, that works too */
2500 	  return g_strconcat (dir->fullname, slash, NULL);
2501 	}
2502     }
2503 #endif
2504 
2505   return g_build_filename (cmpl_state->reference_dir->fullname,
2506 			   text,
2507 			   NULL);
2508 }
2509 
2510 /* The three completion selectors
2511  */
2512 static gchar*
cmpl_this_completion(PossibleCompletion * pc)2513 cmpl_this_completion (PossibleCompletion* pc)
2514 {
2515   return pc->text;
2516 }
2517 
2518 static gboolean
cmpl_is_directory(PossibleCompletion * pc)2519 cmpl_is_directory (PossibleCompletion* pc)
2520 {
2521   return pc->is_directory;
2522 }
2523 
2524 static gint
cmpl_is_a_completion(PossibleCompletion * pc)2525 cmpl_is_a_completion (PossibleCompletion* pc)
2526 {
2527   return pc->is_a_completion;
2528 }
2529 
2530 /**********************************************************************/
2531 /*	                 Construction, deletion                       */
2532 /**********************************************************************/
2533 
2534 /* Get the nearest parent of the current directory for which
2535  * we can convert the filename into UTF-8. With paranoia.
2536  * Returns "." when all goes wrong.
2537  */
2538 static gchar *
get_current_dir_utf8(void)2539 get_current_dir_utf8 (void)
2540 {
2541   gchar *dir = g_get_current_dir ();
2542   gchar *dir_utf8 = NULL;
2543 
2544   while (TRUE)
2545     {
2546       gchar *last_slash;
2547 
2548       dir_utf8 = g_filename_to_utf8 (dir, -1, NULL, NULL, NULL);
2549       if (dir_utf8)
2550 	break;
2551 
2552       last_slash = strrchr (dir, G_DIR_SEPARATOR);
2553       if (!last_slash)		/* g_get_current_dir() wasn't absolute! */
2554 	break;
2555 
2556       if (last_slash + 1 == g_path_skip_root (dir)) /* Parent directory is a root directory */
2557 	{
2558 	  if (last_slash[1] == '\0') /* Root misencoded! */
2559 	    break;
2560 	  else
2561 	    last_slash[1] = '\0';
2562 	}
2563       else
2564 	last_slash[0] = '\0';
2565 
2566       g_assert (last_slash);
2567     }
2568 
2569   g_free (dir);
2570 
2571   return dir_utf8 ? dir_utf8 : g_strdup (".");
2572 }
2573 
2574 static CompletionState*
cmpl_init_state(void)2575 cmpl_init_state (void)
2576 {
2577   gchar *utf8_cwd;
2578   CompletionState *new_state;
2579   gint tries = 0;
2580 
2581   new_state = g_new (CompletionState, 1);
2582 
2583   utf8_cwd = get_current_dir_utf8 ();
2584 
2585 tryagain:
2586   tries++;
2587   new_state->reference_dir = NULL;
2588   new_state->completion_dir = NULL;
2589   new_state->active_completion_dir = NULL;
2590   new_state->directory_storage = NULL;
2591   new_state->directory_sent_storage = NULL;
2592   new_state->last_valid_char = 0;
2593   new_state->updated_text = g_new (gchar, MAXPATHLEN);
2594   new_state->updated_text_alloc = MAXPATHLEN;
2595   new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
2596   new_state->the_completion.text_alloc = MAXPATHLEN;
2597   new_state->user_dir_name_buffer = NULL;
2598   new_state->user_directories = NULL;
2599 
2600   new_state->reference_dir = open_dir (utf8_cwd, new_state);
2601 
2602   if (!new_state->reference_dir)
2603     {
2604       /* Directories changing from underneath us, grumble */
2605       strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
2606       if (tries < 2)
2607 	goto tryagain;
2608     }
2609 
2610   g_free (utf8_cwd);
2611   return new_state;
2612 }
2613 
2614 static void
cmpl_free_dir_list(GList * dp0)2615 cmpl_free_dir_list (GList* dp0)
2616 {
2617   GList *dp = dp0;
2618 
2619   while (dp)
2620     {
2621       free_dir (dp->data);
2622       dp = dp->next;
2623     }
2624 
2625   g_list_free (dp0);
2626 }
2627 
2628 static void
cmpl_free_dir_sent_list(GList * dp0)2629 cmpl_free_dir_sent_list (GList* dp0)
2630 {
2631   GList *dp = dp0;
2632 
2633   while (dp)
2634     {
2635       free_dir_sent (dp->data);
2636       dp = dp->next;
2637     }
2638 
2639   g_list_free (dp0);
2640 }
2641 
2642 static void
cmpl_free_state(CompletionState * cmpl_state)2643 cmpl_free_state (CompletionState* cmpl_state)
2644 {
2645   g_return_if_fail (cmpl_state != NULL);
2646 
2647   cmpl_free_dir_list (cmpl_state->directory_storage);
2648   cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
2649 
2650   g_free (cmpl_state->user_dir_name_buffer);
2651   g_free (cmpl_state->user_directories);
2652   g_free (cmpl_state->the_completion.text);
2653   g_free (cmpl_state->updated_text);
2654 
2655   g_free (cmpl_state);
2656 }
2657 
2658 static void
free_dir(CompletionDir * dir)2659 free_dir (CompletionDir* dir)
2660 {
2661   g_free (dir->cmpl_text);
2662   g_free (dir->fullname);
2663   g_free (dir);
2664 }
2665 
2666 static void
free_dir_sent(CompletionDirSent * sent)2667 free_dir_sent (CompletionDirSent* sent)
2668 {
2669   gint i;
2670   for (i = 0; i < sent->entry_count; i++)
2671     {
2672       g_free (sent->entries[i].entry_name);
2673       g_free (sent->entries[i].sort_key);
2674     }
2675   g_free (sent->entries);
2676   g_free (sent);
2677 }
2678 
2679 static void
prune_memory_usage(CompletionState * cmpl_state)2680 prune_memory_usage (CompletionState *cmpl_state)
2681 {
2682   GList* cdsl = cmpl_state->directory_sent_storage;
2683   GList* cdl = cmpl_state->directory_storage;
2684   GList* cdl0 = cdl;
2685   gint len = 0;
2686 
2687   for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
2688     cdsl = cdsl->next;
2689 
2690   if (cdsl)
2691     {
2692       cmpl_free_dir_sent_list (cdsl->next);
2693       cdsl->next = NULL;
2694     }
2695 
2696   cmpl_state->directory_storage = NULL;
2697   while (cdl)
2698     {
2699       if (cdl->data == cmpl_state->reference_dir)
2700 	cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
2701       else
2702 	free_dir (cdl->data);
2703       cdl = cdl->next;
2704     }
2705 
2706   g_list_free (cdl0);
2707 }
2708 
2709 /**********************************************************************/
2710 /*                        The main entrances.                         */
2711 /**********************************************************************/
2712 
2713 static PossibleCompletion*
cmpl_completion_matches(gchar * text_to_complete,gchar ** remaining_text,CompletionState * cmpl_state)2714 cmpl_completion_matches (gchar           *text_to_complete,
2715 			 gchar          **remaining_text,
2716 			 CompletionState *cmpl_state)
2717 {
2718 #ifdef HAVE_PWD_H
2719   gchar* first_slash;
2720 #endif
2721   PossibleCompletion *poss;
2722 
2723   prune_memory_usage (cmpl_state);
2724 
2725   g_assert (text_to_complete != NULL);
2726 
2727   cmpl_state->user_completion_index = -1;
2728   cmpl_state->last_completion_text = text_to_complete;
2729   cmpl_state->the_completion.text[0] = 0;
2730   cmpl_state->last_valid_char = 0;
2731   cmpl_state->updated_text_len = -1;
2732   cmpl_state->updated_text[0] = 0;
2733   cmpl_state->re_complete = FALSE;
2734 
2735 #ifdef HAVE_PWD_H
2736   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2737 
2738   if (text_to_complete[0] == '~' && !first_slash)
2739     {
2740       /* Text starts with ~ and there is no slash, show all the
2741        * home directory completions.
2742        */
2743       poss = attempt_homedir_completion (text_to_complete, cmpl_state);
2744 
2745       update_cmpl (poss, cmpl_state);
2746 
2747       return poss;
2748     }
2749 #endif
2750   cmpl_state->reference_dir =
2751     open_ref_dir (text_to_complete, remaining_text, cmpl_state);
2752 
2753   if (!cmpl_state->reference_dir)
2754     return NULL;
2755 
2756   cmpl_state->completion_dir =
2757     find_completion_dir (*remaining_text, remaining_text, cmpl_state);
2758 
2759   cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2760 
2761   if (!cmpl_state->completion_dir)
2762     return NULL;
2763 
2764   cmpl_state->completion_dir->cmpl_index = -1;
2765   cmpl_state->completion_dir->cmpl_parent = NULL;
2766   cmpl_state->completion_dir->cmpl_text = g_strdup (*remaining_text);
2767 
2768   cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2769 
2770   cmpl_state->reference_dir = cmpl_state->completion_dir;
2771 
2772   poss = attempt_file_completion (cmpl_state);
2773 
2774   update_cmpl (poss, cmpl_state);
2775 
2776   return poss;
2777 }
2778 
2779 static PossibleCompletion*
cmpl_next_completion(CompletionState * cmpl_state)2780 cmpl_next_completion (CompletionState* cmpl_state)
2781 {
2782   PossibleCompletion* poss = NULL;
2783 
2784   cmpl_state->the_completion.text[0] = 0;
2785 
2786 #ifdef HAVE_PWD_H
2787   if (cmpl_state->user_completion_index >= 0)
2788     poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
2789   else
2790     poss = attempt_file_completion (cmpl_state);
2791 #else
2792   poss = attempt_file_completion (cmpl_state);
2793 #endif
2794 
2795   update_cmpl (poss, cmpl_state);
2796 
2797   return poss;
2798 }
2799 
2800 /**********************************************************************/
2801 /*			 Directory Operations                         */
2802 /**********************************************************************/
2803 
2804 /* Open the directory where completion will begin from, if possible. */
2805 static CompletionDir*
open_ref_dir(gchar * text_to_complete,gchar ** remaining_text,CompletionState * cmpl_state)2806 open_ref_dir (gchar           *text_to_complete,
2807 	      gchar          **remaining_text,
2808 	      CompletionState *cmpl_state)
2809 {
2810   gchar* first_slash;
2811   CompletionDir *new_dir;
2812 
2813   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2814 
2815 #ifdef G_WITH_CYGWIN
2816   if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
2817     {
2818       char root_dir[5];
2819       g_snprintf (root_dir, sizeof (root_dir), "//%c", text_to_complete[2]);
2820 
2821       new_dir = open_dir (root_dir, cmpl_state);
2822 
2823       if (new_dir) {
2824 	*remaining_text = text_to_complete + 4;
2825       }
2826     }
2827 #else
2828   if (FALSE)
2829     ;
2830 #endif
2831 #ifdef HAVE_PWD_H
2832   else if (text_to_complete[0] == '~')
2833     {
2834       new_dir = open_user_dir (text_to_complete, cmpl_state);
2835 
2836       if (new_dir)
2837 	{
2838 	  if (first_slash)
2839 	    *remaining_text = first_slash + 1;
2840 	  else
2841 	    *remaining_text = text_to_complete + strlen (text_to_complete);
2842 	}
2843       else
2844 	{
2845 	  return NULL;
2846 	}
2847     }
2848 #endif
2849   else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2850     {
2851       gchar *tmp = g_strdup (text_to_complete);
2852       gchar *p;
2853 
2854       p = tmp;
2855       while (*p && *p != '*' && *p != '?')
2856 	p++;
2857 
2858       *p = '\0';
2859       p = strrchr (tmp, G_DIR_SEPARATOR);
2860       if (p)
2861 	{
2862 	  if (p + 1 == g_path_skip_root (tmp))
2863 	    p++;
2864 
2865 	  *p = '\0';
2866 	  new_dir = open_dir (tmp, cmpl_state);
2867 
2868 	  if (new_dir)
2869 	    *remaining_text = text_to_complete +
2870 	      ((p == g_path_skip_root (tmp)) ? (p - tmp) : (p + 1 - tmp));
2871 	}
2872       else
2873 	{
2874 	  /* If no possible candidates, use the cwd */
2875 	  gchar *utf8_curdir = get_current_dir_utf8 ();
2876 
2877 	  new_dir = open_dir (utf8_curdir, cmpl_state);
2878 
2879 	  if (new_dir)
2880 	    *remaining_text = text_to_complete;
2881 
2882 	  g_free (utf8_curdir);
2883 	}
2884 
2885       g_free (tmp);
2886     }
2887   else
2888     {
2889       *remaining_text = text_to_complete;
2890 
2891       new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2892     }
2893 
2894   if (new_dir)
2895     {
2896       new_dir->cmpl_index = -1;
2897       new_dir->cmpl_parent = NULL;
2898     }
2899 
2900   return new_dir;
2901 }
2902 
2903 #ifdef HAVE_PWD_H
2904 
2905 /* open a directory by user name */
2906 static CompletionDir*
open_user_dir(const gchar * text_to_complete,CompletionState * cmpl_state)2907 open_user_dir (const gchar     *text_to_complete,
2908 	       CompletionState *cmpl_state)
2909 {
2910   CompletionDir *result;
2911   gchar *first_slash;
2912   gint cmp_len;
2913 
2914   g_assert (text_to_complete && text_to_complete[0] == '~');
2915 
2916   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2917 
2918   if (first_slash)
2919     cmp_len = first_slash - text_to_complete - 1;
2920   else
2921     cmp_len = strlen (text_to_complete + 1);
2922 
2923   if (!cmp_len)
2924     {
2925       /* ~/ */
2926       const gchar *homedir = g_get_home_dir ();
2927       gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
2928 
2929       if (utf8_homedir)
2930 	result = open_dir (utf8_homedir, cmpl_state);
2931       else
2932 	result = NULL;
2933 
2934       g_free (utf8_homedir);
2935     }
2936   else
2937     {
2938       /* ~user/ */
2939       gchar* copy = g_new (char, cmp_len + 1);
2940       gchar *utf8_dir;
2941       struct passwd *pwd;
2942 
2943       strncpy (copy, text_to_complete + 1, cmp_len);
2944       copy[cmp_len] = 0;
2945       pwd = getpwnam (copy);
2946       g_free (copy);
2947       if (!pwd)
2948 	{
2949 	  cmpl_errno = errno;
2950 	  return NULL;
2951 	}
2952       utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
2953       result = open_dir (utf8_dir, cmpl_state);
2954       g_free (utf8_dir);
2955     }
2956   return result;
2957 }
2958 
2959 #endif
2960 
2961 /* open a directory relative to the current relative directory */
2962 static CompletionDir*
open_relative_dir(gchar * dir_name,CompletionDir * dir,CompletionState * cmpl_state)2963 open_relative_dir (gchar           *dir_name,
2964 		   CompletionDir   *dir,
2965 		   CompletionState *cmpl_state)
2966 {
2967   CompletionDir *result;
2968   GString *path;
2969 
2970   path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
2971   g_string_assign (path, dir->fullname);
2972 
2973   if (dir->fullname_len > 1
2974       && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
2975     g_string_append_c (path, G_DIR_SEPARATOR);
2976   g_string_append (path, dir_name);
2977 
2978   result = open_dir (path->str, cmpl_state);
2979 
2980   g_string_free (path, TRUE);
2981 
2982   return result;
2983 }
2984 
2985 /* after the cache lookup fails, really open a new directory */
2986 static CompletionDirSent*
open_new_dir(gchar * dir_name,GStatBuf * sbuf,gboolean stat_subdirs)2987 open_new_dir (gchar    *dir_name,
2988 	      GStatBuf *sbuf,
2989 	      gboolean  stat_subdirs)
2990 {
2991   CompletionDirSent *sent;
2992   GDir *directory;
2993   const char *dirent;
2994   GError *error = NULL;
2995   gint entry_count = 0;
2996   gint n_entries = 0;
2997   gint i;
2998   GStatBuf ent_sbuf;
2999   GString *path;
3000   gchar *sys_dir_name;
3001 
3002   sent = g_new (CompletionDirSent, 1);
3003 #ifndef G_PLATFORM_WIN32
3004   sent->mtime = sbuf->st_mtime;
3005   sent->inode = sbuf->st_ino;
3006   sent->device = sbuf->st_dev;
3007 #endif
3008   path = g_string_sized_new (2*MAXPATHLEN + 10);
3009 
3010   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3011   if (!sys_dir_name)
3012     {
3013       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3014       g_free (sent);
3015       return NULL;
3016     }
3017 
3018   directory = g_dir_open (sys_dir_name, 0, &error);
3019   if (!directory)
3020     {
3021       cmpl_errno = error->code; /* ??? */
3022       g_free (sys_dir_name);
3023       g_free (sent);
3024       return NULL;
3025     }
3026 
3027   while ((dirent = g_dir_read_name (directory)) != NULL)
3028     entry_count++;
3029   entry_count += 2;		/* For ".",".." */
3030 
3031   sent->entries = g_new (CompletionDirEntry, entry_count);
3032   sent->entry_count = entry_count;
3033 
3034   g_dir_rewind (directory);
3035 
3036   for (i = 0; i < entry_count; i += 1)
3037     {
3038       GError *error = NULL;
3039 
3040       if (i == 0)
3041 	dirent = ".";
3042       else if (i == 1)
3043 	dirent = "..";
3044       else
3045 	{
3046 	  dirent = g_dir_read_name (directory);
3047 	  if (!dirent)		/* Directory changed */
3048 	    break;
3049 	}
3050 
3051       sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, &error);
3052       if (sent->entries[n_entries].entry_name == NULL
3053 	  || !g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
3054 	{
3055 	  gchar *escaped_str = g_strescape (dirent, NULL);
3056 	  g_message (_("The filename \"%s\" couldn't be converted to UTF-8. "
3057 		       "(try setting the environment variable G_FILENAME_ENCODING): %s"),
3058 		     escaped_str,
3059 		     error->message ? error->message : _("Invalid UTF-8"));
3060 	  g_free (escaped_str);
3061 	  g_clear_error (&error);
3062 	  continue;
3063 	}
3064       g_clear_error (&error);
3065 
3066       sent->entries[n_entries].sort_key = g_utf8_collate_key (sent->entries[n_entries].entry_name, -1);
3067 
3068       g_string_assign (path, sys_dir_name);
3069       if (path->str[path->len-1] != G_DIR_SEPARATOR)
3070 	{
3071 	  g_string_append_c (path, G_DIR_SEPARATOR);
3072 	}
3073       g_string_append (path, dirent);
3074 
3075       if (stat_subdirs)
3076 	{
3077 	  /* Here we know path->str is a "system charset" string */
3078 	  if (g_stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
3079 	    sent->entries[n_entries].is_dir = TRUE;
3080 	  else
3081 	    /* stat may fail, and we don't mind, since it could be a
3082 	     * dangling symlink. */
3083 	    sent->entries[n_entries].is_dir = FALSE;
3084 	}
3085       else
3086 	sent->entries[n_entries].is_dir = 1;
3087 
3088       n_entries++;
3089     }
3090   sent->entry_count = n_entries;
3091 
3092   g_free (sys_dir_name);
3093   g_string_free (path, TRUE);
3094   qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
3095 
3096   g_dir_close (directory);
3097 
3098   return sent;
3099 }
3100 
3101 #ifndef G_PLATFORM_WIN32
3102 
3103 static gboolean
check_dir(gchar * dir_name,GStatBuf * result,gboolean * stat_subdirs)3104 check_dir (gchar    *dir_name,
3105 	   GStatBuf *result,
3106 	   gboolean *stat_subdirs)
3107 {
3108   /* A list of directories that we know only contain other directories.
3109    * Trying to stat every file in these directories would be very
3110    * expensive.
3111    */
3112 
3113   static struct {
3114     const gchar name[5];
3115     gboolean present;
3116     GStatBuf statbuf;
3117   } no_stat_dirs[] = {
3118     { "/afs", FALSE, { 0 } },
3119     { "/net", FALSE, { 0 } }
3120   };
3121 
3122   static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
3123   static gboolean initialized = FALSE;
3124   gchar *sys_dir_name;
3125   gint i;
3126 
3127   if (!initialized)
3128     {
3129       initialized = TRUE;
3130       for (i = 0; i < n_no_stat_dirs; i++)
3131 	{
3132 	  if (g_stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
3133 	    no_stat_dirs[i].present = TRUE;
3134 	}
3135     }
3136 
3137   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3138   if (!sys_dir_name)
3139     {
3140       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3141       return FALSE;
3142     }
3143 
3144   if (g_stat (sys_dir_name, result) < 0)
3145     {
3146       g_free (sys_dir_name);
3147       cmpl_errno = errno;
3148       return FALSE;
3149     }
3150   g_free (sys_dir_name);
3151 
3152   *stat_subdirs = TRUE;
3153   for (i = 0; i < n_no_stat_dirs; i++)
3154     {
3155       if (no_stat_dirs[i].present &&
3156 	  (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
3157 	  (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
3158 	{
3159 	  *stat_subdirs = FALSE;
3160 	  break;
3161 	}
3162     }
3163 
3164   return TRUE;
3165 }
3166 
3167 #endif
3168 
3169 /* open a directory by absolute pathname */
3170 static CompletionDir*
open_dir(gchar * dir_name,CompletionState * cmpl_state)3171 open_dir (gchar           *dir_name,
3172 	  CompletionState *cmpl_state)
3173 {
3174 #ifndef G_PLATFORM_WIN32
3175   GStatBuf sbuf;
3176   gboolean stat_subdirs;
3177   GList* cdsl;
3178 #endif
3179   CompletionDirSent *sent;
3180 
3181 #ifndef G_PLATFORM_WIN32
3182   if (!check_dir (dir_name, &sbuf, &stat_subdirs))
3183     return NULL;
3184 
3185   cdsl = cmpl_state->directory_sent_storage;
3186 
3187   while (cdsl)
3188     {
3189       sent = cdsl->data;
3190 
3191       if (sent->inode == sbuf.st_ino &&
3192 	  sent->mtime == sbuf.st_mtime &&
3193 	  sent->device == sbuf.st_dev)
3194 	return attach_dir (sent, dir_name, cmpl_state);
3195 
3196       cdsl = cdsl->next;
3197     }
3198 
3199   sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
3200 #else
3201   sent = open_new_dir (dir_name, NULL, TRUE);
3202 #endif
3203 
3204   if (sent)
3205     {
3206       cmpl_state->directory_sent_storage =
3207 	g_list_prepend (cmpl_state->directory_sent_storage, sent);
3208 
3209       return attach_dir (sent, dir_name, cmpl_state);
3210     }
3211 
3212   return NULL;
3213 }
3214 
3215 static CompletionDir*
attach_dir(CompletionDirSent * sent,gchar * dir_name,CompletionState * cmpl_state)3216 attach_dir (CompletionDirSent *sent,
3217 	    gchar             *dir_name,
3218 	    CompletionState   *cmpl_state)
3219 {
3220   CompletionDir* new_dir;
3221 
3222   new_dir = g_new (CompletionDir, 1);
3223 
3224   cmpl_state->directory_storage =
3225     g_list_prepend (cmpl_state->directory_storage, new_dir);
3226 
3227   new_dir->sent = sent;
3228   new_dir->fullname = g_strdup (dir_name);
3229   new_dir->fullname_len = strlen (dir_name);
3230   new_dir->cmpl_text = NULL;
3231 
3232   return new_dir;
3233 }
3234 
3235 static gint
correct_dir_fullname(CompletionDir * cmpl_dir)3236 correct_dir_fullname (CompletionDir* cmpl_dir)
3237 {
3238   gint length = strlen (cmpl_dir->fullname);
3239   gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3240   gchar *sys_filename;
3241   GStatBuf sbuf;
3242 
3243   /* Does it end with /. (\.) ? */
3244   if (length >= 2 &&
3245       strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
3246     {
3247       /* Is it just the root directory (on a drive) ? */
3248       if (cmpl_dir->fullname + length - 2 == first_slash)
3249 	{
3250 	  cmpl_dir->fullname[length - 1] = 0;
3251 	  cmpl_dir->fullname_len = length - 1;
3252 	  return TRUE;
3253 	}
3254       else
3255 	{
3256 	  cmpl_dir->fullname[length - 2] = 0;
3257 	}
3258     }
3259 
3260   /* Ends with /./ (\.\)? */
3261   else if (length >= 3 &&
3262 	   strcmp (cmpl_dir->fullname + length - 3,
3263 		   G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
3264     cmpl_dir->fullname[length - 2] = 0;
3265 
3266   /* Ends with /.. (\..) ? */
3267   else if (length >= 3 &&
3268 	   strcmp (cmpl_dir->fullname + length - 3,
3269 		   G_DIR_SEPARATOR_S "..") == 0)
3270     {
3271       /* Is it just /.. (X:\..)? */
3272       if (cmpl_dir->fullname + length - 3 == first_slash)
3273 	{
3274 	  cmpl_dir->fullname[length - 2] = 0;
3275 	  cmpl_dir->fullname_len = length - 2;
3276 	  return TRUE;
3277 	}
3278 
3279       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3280       if (!sys_filename)
3281 	{
3282 	  cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3283 	  return FALSE;
3284 	}
3285 
3286       if (g_stat (sys_filename, &sbuf) < 0)
3287 	{
3288 	  g_free (sys_filename);
3289 	  cmpl_errno = errno;
3290 	  return FALSE;
3291 	}
3292       g_free (sys_filename);
3293 
3294       cmpl_dir->fullname[length - 3] = 0;
3295 
3296       if (!correct_parent (cmpl_dir, &sbuf))
3297 	return FALSE;
3298     }
3299 
3300   /* Ends with /../ (\..\)? */
3301   else if (length >= 4 &&
3302 	   strcmp (cmpl_dir->fullname + length - 4,
3303 		   G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
3304     {
3305       /* Is it just /../ (X:\..\)? */
3306       if (cmpl_dir->fullname + length - 4 == first_slash)
3307 	{
3308 	  cmpl_dir->fullname[length - 3] = 0;
3309 	  cmpl_dir->fullname_len = length - 3;
3310 	  return TRUE;
3311 	}
3312 
3313       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3314       if (!sys_filename)
3315 	{
3316 	  cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3317 	  return FALSE;
3318 	}
3319 
3320       if (g_stat (sys_filename, &sbuf) < 0)
3321 	{
3322 	  g_free (sys_filename);
3323 	  cmpl_errno = errno;
3324 	  return FALSE;
3325 	}
3326       g_free (sys_filename);
3327 
3328       cmpl_dir->fullname[length - 4] = 0;
3329 
3330       if (!correct_parent (cmpl_dir, &sbuf))
3331 	return FALSE;
3332     }
3333 
3334   cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
3335 
3336   return TRUE;
3337 }
3338 
3339 static gint
correct_parent(CompletionDir * cmpl_dir,GStatBuf * sbuf)3340 correct_parent (CompletionDir *cmpl_dir,
3341 		GStatBuf      *sbuf)
3342 {
3343   GStatBuf parbuf;
3344   gchar *last_slash;
3345   gchar *first_slash;
3346 #ifndef G_PLATFORM_WIN32
3347   gchar *new_name;
3348 #endif
3349   gchar *sys_filename;
3350   gchar c = 0;
3351 
3352   last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3353   g_assert (last_slash);
3354   first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3355 
3356   /* Clever (?) way to check for top-level directory that works also on
3357    * Win32, where there is a drive letter and colon prefixed...
3358    */
3359   if (last_slash != first_slash)
3360     {
3361       last_slash[0] = 0;
3362     }
3363   else
3364     {
3365       c = last_slash[1];
3366       last_slash[1] = 0;
3367     }
3368 
3369   sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3370   if (!sys_filename)
3371     {
3372       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3373       if (!c)
3374 	last_slash[0] = G_DIR_SEPARATOR;
3375       return FALSE;
3376     }
3377 
3378   if (g_stat (sys_filename, &parbuf) < 0)
3379     {
3380       g_free (sys_filename);
3381       cmpl_errno = errno;
3382       if (!c)
3383 	last_slash[0] = G_DIR_SEPARATOR;
3384       return FALSE;
3385     }
3386   g_free (sys_filename);
3387 
3388 #ifndef G_PLATFORM_WIN32	/* No inode numbers on Win32 */
3389   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
3390     /* it wasn't a link */
3391     return TRUE;
3392 
3393   if (c)
3394     last_slash[1] = c;
3395   else
3396     last_slash[0] = G_DIR_SEPARATOR;
3397 
3398   /* it was a link, have to figure it out the hard way */
3399 
3400   new_name = find_parent_dir_fullname (cmpl_dir->fullname);
3401 
3402   if (!new_name)
3403     return FALSE;
3404 
3405   g_free (cmpl_dir->fullname);
3406 
3407   cmpl_dir->fullname = new_name;
3408 #endif
3409 
3410   return TRUE;
3411 }
3412 
3413 #ifndef G_PLATFORM_WIN32
3414 
3415 static gchar*
find_parent_dir_fullname(gchar * dirname)3416 find_parent_dir_fullname (gchar* dirname)
3417 {
3418   gchar *sys_orig_dir;
3419   gchar *result;
3420   gchar *sys_cwd;
3421   gchar *sys_dirname;
3422 
3423   sys_orig_dir = g_get_current_dir ();
3424   sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
3425   if (!sys_dirname)
3426     {
3427       g_free (sys_orig_dir);
3428       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3429       return NULL;
3430     }
3431 
3432   if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
3433     {
3434       int ignored;
3435 
3436       cmpl_errno = errno;
3437       ignored = g_chdir (sys_orig_dir);
3438       g_free (sys_dirname);
3439       g_free (sys_orig_dir);
3440       return NULL;
3441     }
3442   g_free (sys_dirname);
3443 
3444   sys_cwd = g_get_current_dir ();
3445   result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
3446   g_free (sys_cwd);
3447 
3448   if (chdir (sys_orig_dir) != 0)
3449     {
3450       cmpl_errno = errno;
3451       g_free (sys_orig_dir);
3452       return NULL;
3453     }
3454 
3455   g_free (sys_orig_dir);
3456   return result;
3457 }
3458 
3459 #endif
3460 
3461 /**********************************************************************/
3462 /*                        Completion Operations                       */
3463 /**********************************************************************/
3464 
3465 #ifdef HAVE_PWD_H
3466 
3467 static PossibleCompletion*
attempt_homedir_completion(gchar * text_to_complete,CompletionState * cmpl_state)3468 attempt_homedir_completion (gchar           *text_to_complete,
3469 			    CompletionState *cmpl_state)
3470 {
3471   gint index;
3472 
3473   if (!cmpl_state->user_dir_name_buffer &&
3474       !get_pwdb (cmpl_state))
3475     return NULL;
3476 
3477   cmpl_state->user_completion_index += 1;
3478 
3479   while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
3480     {
3481       index = first_diff_index (text_to_complete + 1,
3482 				cmpl_state->user_directories
3483 				[cmpl_state->user_completion_index].login);
3484 
3485       switch (index)
3486 	{
3487 	case PATTERN_MATCH:
3488 	  break;
3489 	default:
3490 	  if (cmpl_state->last_valid_char < (index + 1))
3491 	    cmpl_state->last_valid_char = index + 1;
3492 	  cmpl_state->user_completion_index += 1;
3493 	  continue;
3494 	}
3495 
3496       cmpl_state->the_completion.is_a_completion = 1;
3497       cmpl_state->the_completion.is_directory = TRUE;
3498 
3499       append_completion_text ("~", cmpl_state);
3500 
3501       append_completion_text (cmpl_state->
3502 			      user_directories[cmpl_state->user_completion_index].login,
3503 			      cmpl_state);
3504 
3505       return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3506     }
3507 
3508   if (text_to_complete[1]
3509       || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
3510     {
3511       cmpl_state->user_completion_index = -1;
3512       return NULL;
3513     }
3514   else
3515     {
3516       cmpl_state->user_completion_index += 1;
3517       cmpl_state->the_completion.is_a_completion = 1;
3518       cmpl_state->the_completion.is_directory = TRUE;
3519 
3520       return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
3521     }
3522 }
3523 
3524 #endif
3525 
3526 #ifdef G_PLATFORM_WIN32
3527 /* FIXME: determine whether we should casefold all Unicode letters
3528  * here, too (and in in first_diff_index() walk through the strings with
3529  * g_utf8_next_char()), or if this folding isn't actually needed at
3530  * all.
3531  */
3532 #define FOLD(c) (tolower(c))
3533 #else
3534 #define FOLD(c) (c)
3535 #endif
3536 
3537 /* returns the index (>= 0) of the first differing character,
3538  * PATTERN_MATCH if the completion matches */
3539 static gint
first_diff_index(gchar * pat,gchar * text)3540 first_diff_index (gchar *pat,
3541 		  gchar *text)
3542 {
3543   gint diff = 0;
3544 
3545   while (*pat && *text && FOLD (*text) == FOLD (*pat))
3546     {
3547       pat += 1;
3548       text += 1;
3549       diff += 1;
3550     }
3551 
3552   if (*pat)
3553     return diff;
3554 
3555   return PATTERN_MATCH;
3556 }
3557 
3558 static PossibleCompletion*
append_completion_text(gchar * text,CompletionState * cmpl_state)3559 append_completion_text (gchar           *text,
3560 			CompletionState *cmpl_state)
3561 {
3562   gint len, i = 1;
3563 
3564   if (!cmpl_state->the_completion.text)
3565     return NULL;
3566 
3567   len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
3568 
3569   if (cmpl_state->the_completion.text_alloc > len)
3570     {
3571       strcat (cmpl_state->the_completion.text, text);
3572       return &cmpl_state->the_completion;
3573     }
3574 
3575   while (i < len)
3576     i <<= 1;
3577 
3578   cmpl_state->the_completion.text_alloc = i;
3579 
3580   cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
3581 
3582   if (!cmpl_state->the_completion.text)
3583     return NULL;
3584   else
3585     {
3586       strcat (cmpl_state->the_completion.text, text);
3587       return &cmpl_state->the_completion;
3588     }
3589 }
3590 
3591 static CompletionDir*
find_completion_dir(gchar * text_to_complete,gchar ** remaining_text,CompletionState * cmpl_state)3592 find_completion_dir (gchar          *text_to_complete,
3593 		    gchar          **remaining_text,
3594 		    CompletionState *cmpl_state)
3595 {
3596   gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
3597   CompletionDir* dir = cmpl_state->reference_dir;
3598   CompletionDir* next;
3599   *remaining_text = text_to_complete;
3600 
3601   while (first_slash)
3602     {
3603       gint len = first_slash - *remaining_text;
3604       gint found = 0;
3605       gchar *found_name = NULL;         /* Quiet gcc */
3606       gint i;
3607       gchar* pat_buf = g_new (gchar, len + 1);
3608 
3609       strncpy (pat_buf, *remaining_text, len);
3610       pat_buf[len] = 0;
3611 
3612       for (i = 0; i < dir->sent->entry_count; i += 1)
3613 	{
3614 	  if (dir->sent->entries[i].is_dir &&
3615 	      _gtk_fnmatch (pat_buf, dir->sent->entries[i].entry_name, TRUE))
3616 	    {
3617 	      if (found)
3618 		{
3619 		  g_free (pat_buf);
3620 		  return dir;
3621 		}
3622 	      else
3623 		{
3624 		  found = 1;
3625 		  found_name = dir->sent->entries[i].entry_name;
3626 		}
3627 	    }
3628 	}
3629 
3630       if (!found)
3631 	{
3632 	  /* Perhaps we are trying to open an automount directory */
3633 	  found_name = pat_buf;
3634 	}
3635 
3636       next = open_relative_dir (found_name, dir, cmpl_state);
3637 
3638       if (!next)
3639 	{
3640 	  g_free (pat_buf);
3641 	  return NULL;
3642 }
3643 
3644       next->cmpl_parent = dir;
3645 
3646       dir = next;
3647 
3648       if (!correct_dir_fullname (dir))
3649 	{
3650 	  g_free (pat_buf);
3651 	  return NULL;
3652 	}
3653 
3654       *remaining_text = first_slash + 1;
3655       first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
3656 
3657       g_free (pat_buf);
3658     }
3659 
3660   return dir;
3661 }
3662 
3663 static void
update_cmpl(PossibleCompletion * poss,CompletionState * cmpl_state)3664 update_cmpl (PossibleCompletion *poss,
3665 	     CompletionState    *cmpl_state)
3666 {
3667   gint cmpl_len;
3668 
3669   if (!poss || !cmpl_is_a_completion (poss))
3670     return;
3671 
3672   cmpl_len = strlen (cmpl_this_completion (poss));
3673 
3674   if (cmpl_state->updated_text_alloc < cmpl_len + 1)
3675     {
3676       cmpl_state->updated_text_alloc = 2*cmpl_len;
3677       cmpl_state->updated_text =
3678 	(gchar*)g_realloc (cmpl_state->updated_text,
3679 			   cmpl_state->updated_text_alloc);
3680     }
3681 
3682   if (cmpl_state->updated_text_len < 0)
3683     {
3684       strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3685       cmpl_state->updated_text_len = cmpl_len;
3686       cmpl_state->re_complete = cmpl_is_directory (poss);
3687     }
3688   else if (cmpl_state->updated_text_len == 0)
3689     {
3690       cmpl_state->re_complete = FALSE;
3691     }
3692   else
3693     {
3694       gint first_diff =
3695 	first_diff_index (cmpl_state->updated_text,
3696 			  cmpl_this_completion (poss));
3697 
3698       cmpl_state->re_complete = FALSE;
3699 
3700       if (first_diff == PATTERN_MATCH)
3701 	return;
3702 
3703       if (first_diff > cmpl_state->updated_text_len)
3704 	strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3705 
3706       cmpl_state->updated_text_len = first_diff;
3707       cmpl_state->updated_text[first_diff] = 0;
3708     }
3709 }
3710 
3711 static PossibleCompletion*
attempt_file_completion(CompletionState * cmpl_state)3712 attempt_file_completion (CompletionState *cmpl_state)
3713 {
3714   gchar *pat_buf, *first_slash;
3715   CompletionDir *dir = cmpl_state->active_completion_dir;
3716 
3717   dir->cmpl_index += 1;
3718 
3719   if (dir->cmpl_index == dir->sent->entry_count)
3720     {
3721       if (dir->cmpl_parent == NULL)
3722 	{
3723 	  cmpl_state->active_completion_dir = NULL;
3724 
3725 	  return NULL;
3726 	}
3727       else
3728 	{
3729 	  cmpl_state->active_completion_dir = dir->cmpl_parent;
3730 
3731 	  return attempt_file_completion (cmpl_state);
3732 	}
3733     }
3734 
3735   g_assert (dir->cmpl_text);
3736 
3737   first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
3738 
3739   if (first_slash)
3740     {
3741       gint len = first_slash - dir->cmpl_text;
3742 
3743       pat_buf = g_new (gchar, len + 1);
3744       strncpy (pat_buf, dir->cmpl_text, len);
3745       pat_buf[len] = 0;
3746     }
3747   else
3748     {
3749       gint len = strlen (dir->cmpl_text);
3750 
3751       pat_buf = g_new (gchar, len + 2);
3752       strcpy (pat_buf, dir->cmpl_text);
3753       /* Don't append a * if the user entered one herself.
3754        * This way one can complete *.h and don't get matches
3755        * on any .help files, for instance.
3756        */
3757       if (strchr (pat_buf, '*') == NULL)
3758 	strcpy (pat_buf + len, "*");
3759     }
3760 
3761   if (first_slash)
3762     {
3763       if (dir->sent->entries[dir->cmpl_index].is_dir)
3764 	{
3765 	  if (_gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE))
3766 	    {
3767 	      CompletionDir* new_dir;
3768 
3769 	      new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
3770 					   dir, cmpl_state);
3771 
3772 	      if (!new_dir)
3773 		{
3774 		  g_free (pat_buf);
3775 		  return NULL;
3776 		}
3777 
3778 	      new_dir->cmpl_parent = dir;
3779 
3780 	      new_dir->cmpl_index = -1;
3781 	      new_dir->cmpl_text = g_strdup (first_slash + 1);
3782 
3783 	      cmpl_state->active_completion_dir = new_dir;
3784 
3785 	      g_free (pat_buf);
3786 	      return attempt_file_completion (cmpl_state);
3787 	    }
3788 	  else
3789 	    {
3790 	      g_free (pat_buf);
3791 	      return attempt_file_completion (cmpl_state);
3792 	    }
3793 	}
3794       else
3795 	{
3796 	  g_free (pat_buf);
3797 	  return attempt_file_completion (cmpl_state);
3798 	}
3799     }
3800   else
3801     {
3802       if (dir->cmpl_parent != NULL)
3803 	{
3804 	  append_completion_text (dir->fullname +
3805 				  strlen (cmpl_state->completion_dir->fullname) + 1,
3806 				  cmpl_state);
3807 	  append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3808 	}
3809 
3810       append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
3811 
3812       cmpl_state->the_completion.is_a_completion =
3813 	_gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE);
3814 
3815       cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3816       if (dir->sent->entries[dir->cmpl_index].is_dir)
3817 	append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3818 
3819       g_free (pat_buf);
3820       return &cmpl_state->the_completion;
3821     }
3822 }
3823 
3824 #ifdef HAVE_PWD_H
3825 
3826 static gint
get_pwdb(CompletionState * cmpl_state)3827 get_pwdb (CompletionState* cmpl_state)
3828 {
3829   struct passwd *pwd_ptr;
3830   gchar* buf_ptr;
3831   gchar *utf8;
3832   gint len = 0, i, count = 0;
3833 
3834   if (cmpl_state->user_dir_name_buffer)
3835     return TRUE;
3836   setpwent ();
3837 
3838   while ((pwd_ptr = getpwent ()) != NULL)
3839     {
3840       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3841       len += strlen (utf8);
3842       g_free (utf8);
3843       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3844       len += strlen (utf8);
3845       g_free (utf8);
3846       len += 2;
3847       count += 1;
3848     }
3849 
3850   setpwent ();
3851 
3852   cmpl_state->user_dir_name_buffer = g_new (gchar, len);
3853   cmpl_state->user_directories = g_new (CompletionUserDir, count);
3854   cmpl_state->user_directories_len = count;
3855 
3856   buf_ptr = cmpl_state->user_dir_name_buffer;
3857 
3858   for (i = 0; i < count; i += 1)
3859     {
3860       pwd_ptr = getpwent ();
3861       if (!pwd_ptr)
3862 	{
3863 	  cmpl_errno = errno;
3864 	  goto error;
3865 	}
3866 
3867       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3868       strcpy (buf_ptr, utf8);
3869       g_free (utf8);
3870 
3871       cmpl_state->user_directories[i].login = buf_ptr;
3872 
3873       buf_ptr += strlen (buf_ptr);
3874       buf_ptr += 1;
3875 
3876       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3877       strcpy (buf_ptr, utf8);
3878       g_free (utf8);
3879 
3880       cmpl_state->user_directories[i].homedir = buf_ptr;
3881 
3882       buf_ptr += strlen (buf_ptr);
3883       buf_ptr += 1;
3884     }
3885 
3886   qsort (cmpl_state->user_directories,
3887 	 cmpl_state->user_directories_len,
3888 	 sizeof (CompletionUserDir),
3889 	 compare_user_dir);
3890 
3891   endpwent ();
3892 
3893   return TRUE;
3894 
3895 error:
3896 
3897   g_free (cmpl_state->user_dir_name_buffer);
3898   g_free (cmpl_state->user_directories);
3899 
3900   cmpl_state->user_dir_name_buffer = NULL;
3901   cmpl_state->user_directories = NULL;
3902 
3903   return FALSE;
3904 }
3905 
3906 static gint
compare_user_dir(const void * a,const void * b)3907 compare_user_dir (const void *a,
3908 		  const void *b)
3909 {
3910   return strcmp ((((CompletionUserDir*)a))->login,
3911 		 (((CompletionUserDir*)b))->login);
3912 }
3913 
3914 #endif
3915 
3916 static gint
compare_cmpl_dir(const void * a,const void * b)3917 compare_cmpl_dir (const void *a,
3918 		  const void *b)
3919 {
3920 
3921   return strcmp (((CompletionDirEntry*)a)->sort_key,
3922 		 (((CompletionDirEntry*)b))->sort_key);
3923 }
3924 
3925 static gint
cmpl_state_okay(CompletionState * cmpl_state)3926 cmpl_state_okay (CompletionState* cmpl_state)
3927 {
3928   return  cmpl_state && cmpl_state->reference_dir;
3929 }
3930 
3931 static const gchar*
cmpl_strerror(gint err)3932 cmpl_strerror (gint err)
3933 {
3934   if (err == CMPL_ERRNO_TOO_LONG)
3935     return _("Name too long");
3936   else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
3937     return _("Couldn't convert filename");
3938   else
3939     return g_strerror (err);
3940 }
3941 
3942 #if defined (G_OS_WIN32) && !defined (_WIN64)
3943 
3944 /* DLL ABI stability backward compatibility versions */
3945 
3946 #undef gtk_file_selection_get_filename
3947 
3948 const gchar*
gtk_file_selection_get_filename(GtkFileSelection * filesel)3949 gtk_file_selection_get_filename (GtkFileSelection *filesel)
3950 {
3951   static gchar retval[MAXPATHLEN*2+1];
3952   gchar *tem;
3953 
3954   tem = g_locale_from_utf8 (gtk_file_selection_get_filename_utf8 (filesel),
3955 			    -1, NULL, NULL, NULL);
3956 
3957   strncpy (retval, tem, sizeof (retval) - 1);
3958   retval[sizeof (retval) - 1] = '\0';
3959   g_free (tem);
3960 
3961   return retval;
3962 }
3963 
3964 #undef gtk_file_selection_set_filename
3965 
3966 void
gtk_file_selection_set_filename(GtkFileSelection * filesel,const gchar * filename)3967 gtk_file_selection_set_filename (GtkFileSelection *filesel,
3968 				 const gchar      *filename)
3969 {
3970   gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL);
3971   gtk_file_selection_set_filename_utf8 (filesel, utf8_filename);
3972   g_free (utf8_filename);
3973 }
3974 
3975 #undef gtk_file_selection_get_selections
3976 
3977 gchar **
gtk_file_selection_get_selections(GtkFileSelection * filesel)3978 gtk_file_selection_get_selections (GtkFileSelection *filesel)
3979 {
3980   int i = 0;
3981   gchar **selections = gtk_file_selection_get_selections_utf8 (filesel);
3982 
3983   if (selections != NULL)
3984     while (selections[i] != NULL)
3985       {
3986 	gchar *tem = selections[i];
3987 	selections[i] = g_locale_from_utf8 (selections[i],
3988 					    -1, NULL, NULL, NULL);
3989 	g_free (tem);
3990 	i++;
3991       }
3992 
3993   return selections;
3994 }
3995 
3996 #endif /* G_OS_WIN32 && !_WIN64 */
3997 
3998 #define __GTK_FILESEL_C__
3999 #include "gtkaliasdef.c"
4000