]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemunix.c
6e12713999f922211c55a0d8a0248b4281150d6e
[~andy/gtk] / gtk / gtkfilesystemunix.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilesystemunix.c: Default implementation of GtkFileSystem for UNIX-like systems
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22
23 #include "gtkfilesystem.h"
24 #include "gtkfilesystemunix.h"
25 #include "gtkicontheme.h"
26 #include "gtkintl.h"
27
28 #define XDG_PREFIX _gtk_xdg
29 #include "xdgmime/xdgmime.h"
30
31 #include <errno.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <stdio.h>
37
38 #define BOOKMARKS_FILENAME ".gtk-bookmarks"
39 #define BOOKMARKS_TMP_FILENAME ".gtk-bookmarks-XXXXXX"
40
41 typedef struct _GtkFileSystemUnixClass GtkFileSystemUnixClass;
42
43 #define GTK_FILE_SYSTEM_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
44 #define GTK_IS_FILE_SYSTEM_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_UNIX))
45 #define GTK_FILE_SYSTEM_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
46
47 struct _GtkFileSystemUnixClass
48 {
49   GObjectClass parent_class;
50 };
51
52 struct _GtkFileSystemUnix
53 {
54   GObject parent_instance;
55
56   GHashTable *folder_hash;
57 };
58
59 /* Icon type, supplemented by MIME type
60  */
61 typedef enum {
62   ICON_NONE,
63   ICON_REGULAR, /* Use mime type for icon */
64   ICON_BLOCK_DEVICE,
65   ICON_BROKEN_SYMBOLIC_LINK,
66   ICON_CHARACTER_DEVICE,
67   ICON_DIRECTORY,
68   ICON_EXECUTABLE,
69   ICON_FIFO,
70   ICON_SOCKET
71 } IconType;
72
73
74 #define GTK_TYPE_FILE_FOLDER_UNIX             (gtk_file_folder_unix_get_type ())
75 #define GTK_FILE_FOLDER_UNIX(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnix))
76 #define GTK_IS_FILE_FOLDER_UNIX(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_UNIX))
77 #define GTK_FILE_FOLDER_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
78 #define GTK_IS_FILE_FOLDER_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_UNIX))
79 #define GTK_FILE_FOLDER_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
80
81 typedef struct _GtkFileFolderUnix      GtkFileFolderUnix;
82 typedef struct _GtkFileFolderUnixClass GtkFileFolderUnixClass;
83
84 struct _GtkFileFolderUnixClass
85 {
86   GObjectClass parent_class;
87 };
88
89 struct _GtkFileFolderUnix
90 {
91   GObject parent_instance;
92
93   GtkFileSystemUnix *system_unix;
94   GtkFileInfoType types;
95   gchar *filename;
96 };
97
98 static GObjectClass *system_parent_class;
99 static GObjectClass *folder_parent_class;
100
101 static void gtk_file_system_unix_class_init   (GtkFileSystemUnixClass *class);
102 static void gtk_file_system_unix_iface_init   (GtkFileSystemIface     *iface);
103 static void gtk_file_system_unix_init         (GtkFileSystemUnix      *impl);
104 static void gtk_file_system_unix_finalize     (GObject                *object);
105
106 static GSList *             gtk_file_system_unix_list_volumes        (GtkFileSystem     *file_system);
107 static GtkFileSystemVolume *gtk_file_system_unix_get_volume_for_path (GtkFileSystem     *file_system,
108                                                                       const GtkFilePath *path);
109
110 static GtkFileFolder *gtk_file_system_unix_get_folder    (GtkFileSystem      *file_system,
111                                                           const GtkFilePath  *path,
112                                                           GtkFileInfoType     types,
113                                                           GError            **error);
114 static gboolean       gtk_file_system_unix_create_folder (GtkFileSystem      *file_system,
115                                                           const GtkFilePath  *path,
116                                                           GError            **error);
117
118 static void         gtk_file_system_unix_volume_free             (GtkFileSystem       *file_system,
119                                                                   GtkFileSystemVolume *volume);
120 static GtkFilePath *gtk_file_system_unix_volume_get_base_path    (GtkFileSystem       *file_system,
121                                                                   GtkFileSystemVolume *volume);
122 static gboolean     gtk_file_system_unix_volume_get_is_mounted   (GtkFileSystem       *file_system,
123                                                                   GtkFileSystemVolume *volume);
124 static gboolean     gtk_file_system_unix_volume_mount            (GtkFileSystem       *file_system,
125                                                                   GtkFileSystemVolume *volume,
126                                                                   GError             **error);
127 static gchar *      gtk_file_system_unix_volume_get_display_name (GtkFileSystem       *file_system,
128                                                                   GtkFileSystemVolume *volume);
129 static GdkPixbuf *  gtk_file_system_unix_volume_render_icon      (GtkFileSystem        *file_system,
130                                                                   GtkFileSystemVolume  *volume,
131                                                                   GtkWidget            *widget,
132                                                                   gint                  pixel_size,
133                                                                   GError              **error);
134
135 static gboolean       gtk_file_system_unix_get_parent    (GtkFileSystem      *file_system,
136                                                           const GtkFilePath  *path,
137                                                           GtkFilePath       **parent,
138                                                           GError            **error);
139 static GtkFilePath *  gtk_file_system_unix_make_path     (GtkFileSystem      *file_system,
140                                                           const GtkFilePath  *base_path,
141                                                           const gchar        *display_name,
142                                                           GError            **error);
143 static gboolean       gtk_file_system_unix_parse         (GtkFileSystem      *file_system,
144                                                           const GtkFilePath  *base_path,
145                                                           const gchar        *str,
146                                                           GtkFilePath       **folder,
147                                                           gchar             **file_part,
148                                                           GError            **error);
149
150 static gchar *      gtk_file_system_unix_path_to_uri      (GtkFileSystem     *file_system,
151                                                            const GtkFilePath *path);
152 static gchar *      gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
153                                                            const GtkFilePath *path);
154 static GtkFilePath *gtk_file_system_unix_uri_to_path      (GtkFileSystem     *file_system,
155                                                            const gchar       *uri);
156 static GtkFilePath *gtk_file_system_unix_filename_to_path (GtkFileSystem     *file_system,
157                                                            const gchar       *filename);
158
159 static GdkPixbuf *gtk_file_system_unix_render_icon (GtkFileSystem     *file_system,
160                                                     const GtkFilePath *path,
161                                                     GtkWidget         *widget,
162                                                     gint               pixel_size,
163                                                     GError           **error);
164
165 static gboolean gtk_file_system_unix_insert_bookmark (GtkFileSystem     *file_system,
166                                                       const GtkFilePath *path,
167                                                       gint               position,
168                                                       GError           **error);
169 static gboolean gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
170                                                       const GtkFilePath *path,
171                                                       GError           **error);
172 static GSList * gtk_file_system_unix_list_bookmarks  (GtkFileSystem *file_system);
173
174 static GType gtk_file_folder_unix_get_type   (void);
175 static void  gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class);
176 static void  gtk_file_folder_unix_iface_init (GtkFileFolderIface     *iface);
177 static void  gtk_file_folder_unix_init       (GtkFileFolderUnix      *impl);
178 static void  gtk_file_folder_unix_finalize   (GObject                *object);
179
180 static GtkFileInfo *gtk_file_folder_unix_get_info      (GtkFileFolder  *folder,
181                                                         const GtkFilePath    *path,
182                                                         GError        **error);
183 static gboolean     gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
184                                                         GSList        **children,
185                                                         GError        **error);
186
187 static GtkFilePath *filename_to_path   (const gchar       *filename);
188
189 static gboolean     filename_is_root  (const char       *filename);
190 static GtkFileInfo *filename_get_info (const gchar      *filename,
191                                        GtkFileInfoType   types,
192                                        GError          **error);
193
194 /*
195  * GtkFileSystemUnix
196  */
197 GType
198 gtk_file_system_unix_get_type (void)
199 {
200   static GType file_system_unix_type = 0;
201
202   if (!file_system_unix_type)
203     {
204       static const GTypeInfo file_system_unix_info =
205       {
206         sizeof (GtkFileSystemUnixClass),
207         NULL,           /* base_init */
208         NULL,           /* base_finalize */
209         (GClassInitFunc) gtk_file_system_unix_class_init,
210         NULL,           /* class_finalize */
211         NULL,           /* class_data */
212         sizeof (GtkFileSystemUnix),
213         0,              /* n_preallocs */
214         (GInstanceInitFunc) gtk_file_system_unix_init,
215       };
216
217       static const GInterfaceInfo file_system_info =
218       {
219         (GInterfaceInitFunc) gtk_file_system_unix_iface_init, /* interface_init */
220         NULL,                                                 /* interface_finalize */
221         NULL                                                  /* interface_data */
222       };
223
224       file_system_unix_type = g_type_register_static (G_TYPE_OBJECT,
225                                                       "GtkFileSystemUnix",
226                                                       &file_system_unix_info, 0);
227       g_type_add_interface_static (file_system_unix_type,
228                                    GTK_TYPE_FILE_SYSTEM,
229                                    &file_system_info);
230     }
231
232   return file_system_unix_type;
233 }
234
235 /**
236  * gtk_file_system_unix_new:
237  *
238  * Creates a new #GtkFileSystemUnix object. #GtkFileSystemUnix
239  * implements the #GtkFileSystem interface with direct access to
240  * the filesystem using Unix/Linux API calls
241  *
242  * Return value: the new #GtkFileSystemUnix object
243  **/
244 GtkFileSystem *
245 gtk_file_system_unix_new (void)
246 {
247   return g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL);
248 }
249
250 static void
251 gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class)
252 {
253   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
254
255   system_parent_class = g_type_class_peek_parent (class);
256
257   gobject_class->finalize = gtk_file_system_unix_finalize;
258 }
259
260 static void
261 gtk_file_system_unix_iface_init   (GtkFileSystemIface *iface)
262 {
263   iface->list_volumes = gtk_file_system_unix_list_volumes;
264   iface->get_volume_for_path = gtk_file_system_unix_get_volume_for_path;
265   iface->get_folder = gtk_file_system_unix_get_folder;
266   iface->create_folder = gtk_file_system_unix_create_folder;
267   iface->volume_free = gtk_file_system_unix_volume_free;
268   iface->volume_get_base_path = gtk_file_system_unix_volume_get_base_path;
269   iface->volume_get_is_mounted = gtk_file_system_unix_volume_get_is_mounted;
270   iface->volume_mount = gtk_file_system_unix_volume_mount;
271   iface->volume_get_display_name = gtk_file_system_unix_volume_get_display_name;
272   iface->volume_render_icon = gtk_file_system_unix_volume_render_icon;
273   iface->get_parent = gtk_file_system_unix_get_parent;
274   iface->make_path = gtk_file_system_unix_make_path;
275   iface->parse = gtk_file_system_unix_parse;
276   iface->path_to_uri = gtk_file_system_unix_path_to_uri;
277   iface->path_to_filename = gtk_file_system_unix_path_to_filename;
278   iface->uri_to_path = gtk_file_system_unix_uri_to_path;
279   iface->filename_to_path = gtk_file_system_unix_filename_to_path;
280   iface->render_icon = gtk_file_system_unix_render_icon;
281   iface->insert_bookmark = gtk_file_system_unix_insert_bookmark;
282   iface->remove_bookmark = gtk_file_system_unix_remove_bookmark;
283   iface->list_bookmarks = gtk_file_system_unix_list_bookmarks;
284 }
285
286 static void
287 gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
288 {
289   system_unix->folder_hash = g_hash_table_new (g_str_hash, g_str_equal);
290 }
291
292 static void
293 gtk_file_system_unix_finalize (GObject *object)
294 {
295   GtkFileSystemUnix *system_unix;
296
297   system_unix = GTK_FILE_SYSTEM_UNIX (object);
298
299   /* FIXME: assert that the hash is empty? */
300   g_hash_table_destroy (system_unix->folder_hash);
301
302   system_parent_class->finalize (object);
303 }
304
305 /* Returns our single root volume */
306 static GtkFileSystemVolume *
307 get_root_volume (void)
308 {
309   return (GtkFileSystemVolume *) gtk_file_path_new_dup ("/");
310 }
311
312 static GSList *
313 gtk_file_system_unix_list_volumes (GtkFileSystem *file_system)
314 {
315   return g_slist_append (NULL, get_root_volume ());
316 }
317
318 static GtkFileSystemVolume *
319 gtk_file_system_unix_get_volume_for_path (GtkFileSystem     *file_system,
320                                           const GtkFilePath *path)
321 {
322   return get_root_volume ();
323 }
324
325 static GtkFileFolder *
326 gtk_file_system_unix_get_folder (GtkFileSystem     *file_system,
327                                  const GtkFilePath *path,
328                                  GtkFileInfoType    types,
329                                  GError           **error)
330 {
331   GtkFileSystemUnix *system_unix;
332   GtkFileFolderUnix *folder_unix;
333   const char *filename;
334
335   system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
336
337   filename = gtk_file_path_get_string (path);
338   g_return_val_if_fail (filename != NULL, NULL);
339   g_return_val_if_fail (g_path_is_absolute (filename), NULL);
340
341   folder_unix = g_hash_table_lookup (system_unix->folder_hash, filename);
342
343   if (folder_unix)
344     {
345       folder_unix->types |= types;
346       return g_object_ref (folder_unix);
347     }
348   else
349     {
350       folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
351       folder_unix->system_unix = system_unix;
352       folder_unix->filename = g_strdup (filename);
353       folder_unix->types = types;
354
355       g_hash_table_insert (system_unix->folder_hash, folder_unix->filename, folder_unix);
356
357       return GTK_FILE_FOLDER (folder_unix);
358     }
359 }
360
361 static gboolean
362 gtk_file_system_unix_create_folder (GtkFileSystem     *file_system,
363                                     const GtkFilePath *path,
364                                     GError           **error)
365 {
366   GtkFileSystemUnix *system_unix;
367   const char *filename;
368   gboolean result;
369   char *parent;
370
371   system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
372
373   filename = gtk_file_path_get_string (path);
374   g_return_val_if_fail (filename != NULL, FALSE);
375   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
376
377   result = mkdir (filename, 0777) == 0;
378
379   if (!result)
380     {
381       int save_errno = errno;
382       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
383       g_set_error (error,
384                    GTK_FILE_SYSTEM_ERROR,
385                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
386                    _("error creating directory '%s': %s"),
387                    filename_utf8 ? filename_utf8 : "???",
388                    g_strerror (save_errno));
389       g_free (filename_utf8);
390       return FALSE;
391     }
392
393   if (filename_is_root (filename))
394     return TRUE; /* hmmm, but with no notification */
395
396   parent = g_path_get_dirname (filename);
397   if (parent)
398     {
399       GtkFileFolderUnix *folder_unix;
400
401       folder_unix = g_hash_table_lookup (system_unix->folder_hash, parent);
402       if (folder_unix)
403         {
404           GSList *paths;
405
406           paths = g_slist_append (NULL, (GtkFilePath *) path);
407           g_signal_emit_by_name (folder_unix, "files-added", paths);
408           g_slist_free (paths);
409         }
410     }
411
412   return TRUE;
413 }
414
415 static void
416 gtk_file_system_unix_volume_free (GtkFileSystem        *file_system,
417                                   GtkFileSystemVolume  *volume)
418 {
419   GtkFilePath *path;
420
421   path = (GtkFilePath *) volume;
422   gtk_file_path_free (path);
423 }
424
425 static GtkFilePath *
426 gtk_file_system_unix_volume_get_base_path (GtkFileSystem        *file_system,
427                                            GtkFileSystemVolume  *volume)
428 {
429   return gtk_file_path_new_dup ("/");
430 }
431
432 static gboolean
433 gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem        *file_system,
434                                             GtkFileSystemVolume  *volume)
435 {
436   return TRUE;
437 }
438
439 static gboolean
440 gtk_file_system_unix_volume_mount (GtkFileSystem        *file_system,
441                                    GtkFileSystemVolume  *volume,
442                                    GError              **error)
443 {
444   g_set_error (error,
445                GTK_FILE_SYSTEM_ERROR,
446                GTK_FILE_SYSTEM_ERROR_FAILED,
447                _("This file system does not support mounting"));
448   return FALSE;
449 }
450
451 static gchar *
452 gtk_file_system_unix_volume_get_display_name (GtkFileSystem       *file_system,
453                                               GtkFileSystemVolume *volume)
454 {
455   return g_strdup (_("Filesystem")); /* Same as Nautilus */
456 }
457
458 static IconType
459 get_icon_type (const char *filename,
460                GError    **error)
461 {
462   struct stat statbuf;
463   IconType icon_type;
464
465   /* If stat fails, try to fall back to lstat to catch broken links
466    */
467   if (stat (filename, &statbuf) != 0)
468     {
469       if (errno != ENOENT || lstat (filename, &statbuf) != 0)
470         {
471           int save_errno = errno;
472           gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
473           g_set_error (error,
474                        GTK_FILE_SYSTEM_ERROR,
475                        GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
476                        _("error getting information for '%s': %s"),
477                        filename_utf8 ? filename_utf8 : "???",
478                        g_strerror (save_errno));
479           g_free (filename_utf8);
480
481           return ICON_NONE;
482         }
483     }
484
485   if (S_ISBLK (statbuf.st_mode))
486     icon_type = ICON_BLOCK_DEVICE;
487   else if (S_ISLNK (statbuf.st_mode))
488     icon_type = ICON_BROKEN_SYMBOLIC_LINK; /* See above */
489   else if (S_ISCHR (statbuf.st_mode))
490     icon_type = ICON_CHARACTER_DEVICE;
491   else if (S_ISDIR (statbuf.st_mode))
492     icon_type = ICON_DIRECTORY;
493   else if (S_ISFIFO (statbuf.st_mode))
494     icon_type =  ICON_FIFO;
495   else if (S_ISSOCK (statbuf.st_mode))
496     icon_type = ICON_SOCKET;
497   else
498     {
499       icon_type = ICON_REGULAR;
500
501 #if 0
502       if ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR &&
503           (statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) &&
504           (strcmp (mime_type, XDG_MIME_TYPE_UNKNOWN) == 0 ||
505            strcmp (mime_type, "application/x-executable") == 0 ||
506            strcmp (mime_type, "application/x-shellscript") == 0))
507         gtk_file_info_set_icon_type (info, GTK_FILE_ICON_EXECUTABLE);
508 #endif
509     }
510
511   return icon_type;
512 }
513
514 typedef struct
515 {
516   gint size;
517   GdkPixbuf *pixbuf;
518 } IconCacheElement;
519
520 static void
521 icon_cache_element_free (IconCacheElement *element)
522 {
523   if (element->pixbuf)
524     g_object_unref (element->pixbuf);
525   g_free (element);
526 }
527
528 static void
529 icon_theme_changed (GtkIconTheme *icon_theme)
530 {
531   GHashTable *cache;
532
533   /* Difference from the initial creation is that we don't
534    * reconnect the signal
535    */
536   cache = g_hash_table_new_full (g_str_hash, g_str_equal,
537                                  (GDestroyNotify)g_free,
538                                  (GDestroyNotify)icon_cache_element_free);
539   g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
540                           cache, (GDestroyNotify)g_hash_table_destroy);
541 }
542
543 static GdkPixbuf *
544 get_cached_icon (GtkWidget   *widget,
545                  const gchar *name,
546                  gint         pixel_size)
547 {
548   GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
549   GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
550   IconCacheElement *element;
551
552   if (!cache)
553     {
554       cache = g_hash_table_new_full (g_str_hash, g_str_equal,
555                                      (GDestroyNotify)g_free,
556                                      (GDestroyNotify)icon_cache_element_free);
557
558       g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
559                               cache, (GDestroyNotify)g_hash_table_destroy);
560       g_signal_connect (icon_theme, "changed",
561                         G_CALLBACK (icon_theme_changed), NULL);
562     }
563
564   element = g_hash_table_lookup (cache, name);
565   if (!element)
566     {
567       element = g_new0 (IconCacheElement, 1);
568       g_hash_table_insert (cache, g_strdup (name), element);
569     }
570
571   if (element->size != pixel_size)
572     {
573       if (element->pixbuf)
574         g_object_unref (element->pixbuf);
575       element->size = pixel_size;
576       element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
577                                                   pixel_size, 0, NULL);
578     }
579
580   return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
581 }
582
583 static GdkPixbuf *
584 gtk_file_system_unix_volume_render_icon (GtkFileSystem        *file_system,
585                                          GtkFileSystemVolume  *volume,
586                                          GtkWidget            *widget,
587                                          gint                  pixel_size,
588                                          GError              **error)
589 {
590   /* FIXME: set the GError if we can't load the icon */
591   return get_cached_icon (widget, "gnome-fs-blockdev", pixel_size);
592 }
593
594 static gboolean
595 gtk_file_system_unix_get_parent (GtkFileSystem     *file_system,
596                                  const GtkFilePath *path,
597                                  GtkFilePath      **parent,
598                                  GError           **error)
599 {
600   const char *filename;
601
602   filename = gtk_file_path_get_string (path);
603   g_return_val_if_fail (filename != NULL, FALSE);
604   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
605
606   if (filename_is_root (filename))
607     {
608       *parent = NULL;
609     }
610   else
611     {
612       gchar *parent_filename = g_path_get_dirname (filename);
613       *parent = filename_to_path (parent_filename);
614       g_free (parent_filename);
615     }
616
617   return TRUE;
618 }
619
620 static GtkFilePath *
621 gtk_file_system_unix_make_path (GtkFileSystem    *file_system,
622                                const GtkFilePath *base_path,
623                                const gchar       *display_name,
624                                GError           **error)
625 {
626   const char *base_filename;
627   gchar *filename;
628   gchar *full_filename;
629   GError *tmp_error = NULL;
630   GtkFilePath *result;
631
632   base_filename = gtk_file_path_get_string (base_path);
633   g_return_val_if_fail (base_filename != NULL, NULL);
634   g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
635
636   filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
637   if (!filename)
638     {
639       g_set_error (error,
640                    GTK_FILE_SYSTEM_ERROR,
641                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
642                    "%s",
643                    tmp_error->message);
644
645       g_error_free (tmp_error);
646
647       return NULL;
648     }
649
650   full_filename = g_build_filename (base_filename, filename, NULL);
651   result = filename_to_path (full_filename);
652   g_free (filename);
653   g_free (full_filename);
654
655   return result;
656 }
657
658 /* If this was a publically exported function, it should return
659  * a dup'ed result, but we make it modify-in-place for efficiency
660  * here, and because it works for us.
661  */
662 static void
663 canonicalize_filename (gchar *filename)
664 {
665   gchar *p, *q;
666   gboolean last_was_slash = FALSE;
667
668   p = filename;
669   q = filename;
670
671   while (*p)
672     {
673       if (*p == G_DIR_SEPARATOR)
674         {
675           if (!last_was_slash)
676             *q++ = G_DIR_SEPARATOR;
677
678           last_was_slash = TRUE;
679         }
680       else
681         {
682           if (last_was_slash && *p == '.')
683             {
684               if (*(p + 1) == G_DIR_SEPARATOR ||
685                   *(p + 1) == '\0')
686                 {
687                   if (*(p + 1) == '\0')
688                     break;
689
690                   p += 1;
691                 }
692               else if (*(p + 1) == '.' &&
693                        (*(p + 2) == G_DIR_SEPARATOR ||
694                         *(p + 2) == '\0'))
695                 {
696                   if (q > filename + 1)
697                     {
698                       q--;
699                       while (q > filename + 1 &&
700                              *(q - 1) != G_DIR_SEPARATOR)
701                         q--;
702                     }
703
704                   if (*(p + 2) == '\0')
705                     break;
706
707                   p += 2;
708                 }
709               else
710                 {
711                   *q++ = *p;
712                   last_was_slash = FALSE;
713                 }
714             }
715           else
716             {
717               *q++ = *p;
718               last_was_slash = FALSE;
719             }
720         }
721
722       p++;
723     }
724
725   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
726     q--;
727
728   *q = '\0';
729 }
730
731 static gboolean
732 gtk_file_system_unix_parse (GtkFileSystem     *file_system,
733                             const GtkFilePath *base_path,
734                             const gchar       *str,
735                             GtkFilePath      **folder,
736                             gchar            **file_part,
737                             GError           **error)
738 {
739   const char *base_filename;
740   gchar *last_slash;
741   gboolean result = FALSE;
742
743   base_filename = gtk_file_path_get_string (base_path);
744   g_return_val_if_fail (base_filename != NULL, FALSE);
745   g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
746
747   last_slash = strrchr (str, G_DIR_SEPARATOR);
748   if (!last_slash)
749     {
750       *folder = gtk_file_path_copy (base_path);
751       *file_part = g_strdup (str);
752       result = TRUE;
753     }
754   else
755     {
756       gchar *folder_part;
757       gchar *folder_path;
758       GError *tmp_error = NULL;
759
760       if (last_slash == str)
761         folder_part = g_strdup ("/");
762       else
763         folder_part = g_filename_from_utf8 (str, last_slash - str,
764                                             NULL, NULL, &tmp_error);
765
766       if (!folder_part)
767         {
768           g_set_error (error,
769                        GTK_FILE_SYSTEM_ERROR,
770                        GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
771                        "%s",
772                        tmp_error->message);
773           g_error_free (tmp_error);
774         }
775       else
776         {
777           if (folder_part[0] == G_DIR_SEPARATOR)
778             folder_path = folder_part;
779           else
780             {
781               folder_path = g_build_filename (base_filename, folder_part, NULL);
782               g_free (folder_part);
783             }
784
785           canonicalize_filename (folder_path);
786
787           *folder = filename_to_path (folder_path);
788           *file_part = g_strdup (last_slash + 1);
789
790           g_free (folder_path);
791
792           result = TRUE;
793         }
794     }
795
796   return result;
797 }
798
799 static gchar *
800 gtk_file_system_unix_path_to_uri (GtkFileSystem     *file_system,
801                                   const GtkFilePath *path)
802 {
803   return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
804 }
805
806 static gchar *
807 gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
808                                        const GtkFilePath *path)
809 {
810   return g_strdup (gtk_file_path_get_string (path));
811 }
812
813 static GtkFilePath *
814 gtk_file_system_unix_uri_to_path (GtkFileSystem     *file_system,
815                                   const gchar       *uri)
816 {
817   gchar *filename = g_filename_from_uri (uri, NULL, NULL);
818   if (filename)
819     return gtk_file_path_new_steal (filename);
820   else
821     return NULL;
822 }
823
824 static GtkFilePath *
825 gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
826                                        const gchar   *filename)
827 {
828   return gtk_file_path_new_dup (filename);
829 }
830
831 static const char *
832 get_icon_for_directory (const char *path)
833 {
834   static char *desktop_path = NULL;
835
836   if (!g_get_home_dir ())
837     return "gnome-fs-directory";
838
839   if (!desktop_path)
840       desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
841
842   if (strcmp (g_get_home_dir (), path) == 0)
843     return "gnome-fs-home";
844   else if (strcmp (desktop_path, path) == 0)
845     return "gnome-fs-desktop";
846   else
847     return "gnome-fs-directory";
848 }
849
850 static GdkPixbuf *
851 gtk_file_system_unix_render_icon (GtkFileSystem     *file_system,
852                                   const GtkFilePath *path,
853                                   GtkWidget         *widget,
854                                   gint               pixel_size,
855                                   GError           **error)
856 {
857   const char *filename;
858   IconType icon_type;
859   const char *mime_type;
860
861   filename = gtk_file_path_get_string (path);
862   icon_type = get_icon_type (filename, error);
863
864   /* FIXME: this function should not return NULL without setting the GError; we
865    * should perhaps provide a "never fails" generic stock icon for when all else
866    * fails.
867    */
868
869   if (icon_type == ICON_NONE)
870     return NULL;
871
872   if (icon_type != ICON_REGULAR)
873     {
874       const char *name;
875
876       switch (icon_type)
877         {
878         case ICON_BLOCK_DEVICE:
879           name = "gnome-fs-blockdev";
880           break;
881         case ICON_BROKEN_SYMBOLIC_LINK:
882           name = "gnome-fs-symlink";
883           break;
884         case ICON_CHARACTER_DEVICE:
885           name = "gnome-fs-chardev";
886           break;
887         case ICON_DIRECTORY:
888           name = get_icon_for_directory (filename);
889           break;
890         case ICON_EXECUTABLE:
891           name ="gnome-fs-executable";
892           break;
893         case ICON_FIFO:
894           name = "gnome-fs-fifo";
895           break;
896         case ICON_SOCKET:
897           name = "gnome-fs-socket";
898           break;
899         default:
900           g_assert_not_reached ();
901           return NULL;
902         }
903
904       return get_cached_icon (widget, name, pixel_size);
905     }
906
907   mime_type = xdg_mime_get_mime_type_for_file (filename);
908   if (mime_type)
909     {
910       const char *separator;
911       GString *icon_name;
912       GdkPixbuf *pixbuf;
913
914       separator = strchr (mime_type, '/');
915       if (!separator)
916         return NULL;
917
918       icon_name = g_string_new ("gnome-mime-");
919       g_string_append_len (icon_name, mime_type, separator - mime_type);
920       g_string_append_c (icon_name, '-');
921       g_string_append (icon_name, separator + 1);
922       pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
923       g_string_free (icon_name, TRUE);
924       if (pixbuf)
925         return pixbuf;
926
927       icon_name = g_string_new ("gnome-mime-");
928       g_string_append_len (icon_name, mime_type, separator - mime_type);
929       pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
930       g_string_free (icon_name, TRUE);
931       if (pixbuf)
932         return pixbuf;
933     }
934
935   return get_cached_icon (widget, "gnome-fs-regular", pixel_size);
936 }
937
938 static void
939 bookmark_list_free (GSList *list)
940 {
941   GSList *l;
942
943   for (l = list; l; l = l->next)
944     g_free (l->data);
945
946   g_slist_free (list);
947 }
948
949 /* Returns whether a URI is a local file:// */
950 static gboolean
951 is_local_uri (const char *uri)
952 {
953   char *filename;
954   char *hostname;
955   gboolean result;
956
957   /* This is rather crude, but hey */
958   filename = g_filename_from_uri (uri, &hostname, NULL);
959
960   result = (filename && !hostname);
961
962   g_free (filename);
963   g_free (hostname);
964
965   return result;
966 }
967
968 static char *
969 bookmark_get_filename (gboolean tmp_file)
970 {
971   char *filename;
972
973   filename = g_build_filename (g_get_home_dir (),
974                                tmp_file ? BOOKMARKS_TMP_FILENAME : BOOKMARKS_FILENAME,
975                                NULL);
976   g_assert (filename != NULL);
977   return filename;
978 }
979
980 static gboolean
981 bookmark_list_read (GSList **bookmarks, GError **error)
982 {
983   gchar *filename;
984   gchar *contents;
985   gboolean result = FALSE;
986
987   filename = bookmark_get_filename (FALSE);
988   *bookmarks = NULL;
989
990   if (g_file_get_contents (filename, &contents, NULL, error))
991     {
992       gchar **lines = g_strsplit (contents, "\n", -1);
993       int i;
994       GHashTable *table;
995
996       table = g_hash_table_new (g_str_hash, g_str_equal);
997
998       for (i = 0; lines[i]; i++)
999         {
1000           if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
1001             {
1002               *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
1003               g_hash_table_insert (table, lines[i], lines[i]);
1004             }
1005         }
1006
1007       g_free (contents);
1008       g_hash_table_destroy (table);
1009       g_strfreev (lines);
1010
1011       *bookmarks = g_slist_reverse (*bookmarks);
1012       result = TRUE;
1013     }
1014
1015   g_free (filename);
1016
1017   return result;
1018 }
1019
1020 static gboolean
1021 bookmark_list_write (GSList *bookmarks, GError **error)
1022 {
1023   char *tmp_filename;
1024   char *filename;
1025   gboolean result = TRUE;
1026   FILE *file;
1027   int fd;
1028   int saved_errno;
1029
1030   /* First, write a temporary file */
1031
1032   tmp_filename = bookmark_get_filename (TRUE);
1033   filename = bookmark_get_filename (FALSE);
1034
1035   fd = g_mkstemp (tmp_filename);
1036   if (fd == -1)
1037     {
1038       saved_errno = errno;
1039       goto io_error;
1040     }
1041
1042   if ((file = fdopen (fd, "w")) != NULL)
1043     {
1044       GSList *l;
1045
1046       for (l = bookmarks; l; l = l->next)
1047         if (fputs (l->data, file) == EOF
1048             || fputs ("\n", file) == EOF)
1049           {
1050             saved_errno = errno;
1051             goto io_error;
1052           }
1053
1054       if (fclose (file) == EOF)
1055         {
1056           saved_errno = errno;
1057           goto io_error;
1058         }
1059
1060       if (rename (tmp_filename, filename) == -1)
1061         {
1062           saved_errno = errno;
1063           goto io_error;
1064         }
1065
1066       result = TRUE;
1067       goto out;
1068     }
1069   else
1070     {
1071       saved_errno = errno;
1072
1073       /* fdopen() failed, so we can't do much error checking here anyway */
1074       close (fd);
1075     }
1076
1077  io_error:
1078
1079   g_set_error (error,
1080                GTK_FILE_SYSTEM_ERROR,
1081                GTK_FILE_SYSTEM_ERROR_FAILED,
1082                _("Bookmark saving failed (%s)"),
1083                g_strerror (saved_errno));
1084   result = FALSE;
1085
1086   if (fd != -1)
1087     unlink (tmp_filename); /* again, not much error checking we can do here */
1088
1089  out:
1090
1091   g_free (filename);
1092   g_free (tmp_filename);
1093
1094   return result;
1095 }
1096
1097 static gboolean
1098 gtk_file_system_unix_insert_bookmark (GtkFileSystem     *file_system,
1099                                       const GtkFilePath *path,
1100                                       gint               position,
1101                                       GError           **error)
1102 {
1103   GSList *bookmarks;
1104   int num_bookmarks;
1105   GSList *l;
1106   char *uri;
1107   gboolean result;
1108   GError *err;
1109
1110   err = NULL;
1111   if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
1112     {
1113       g_propagate_error (error, err);
1114       return FALSE;
1115     }
1116
1117   num_bookmarks = g_slist_length (bookmarks);
1118   g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
1119
1120   result = FALSE;
1121
1122   uri = gtk_file_system_unix_path_to_uri (file_system, path);
1123
1124   for (l = bookmarks; l; l = l->next)
1125     {
1126       const char *bookmark;
1127
1128       bookmark = l->data;
1129       if (strcmp (bookmark, uri) == 0)
1130         {
1131           g_set_error (error,
1132                        GTK_FILE_SYSTEM_ERROR,
1133                        GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
1134                        "%s already exists in the bookmarks list",
1135                        uri);
1136           goto out;
1137         }
1138     }
1139
1140   bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
1141   if (bookmark_list_write (bookmarks, error))
1142     {
1143       result = TRUE;
1144       g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1145     }
1146
1147  out:
1148
1149   g_free (uri);
1150   bookmark_list_free (bookmarks);
1151
1152   return result;
1153 }
1154
1155 static gboolean
1156 gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
1157                                       const GtkFilePath *path,
1158                                       GError           **error)
1159 {
1160   GSList *bookmarks;
1161   char *uri;
1162   GSList *l;
1163   gboolean result;
1164
1165   if (!bookmark_list_read (&bookmarks, error))
1166     return FALSE;
1167
1168   result = FALSE;
1169
1170   uri = gtk_file_system_path_to_uri (file_system, path);
1171
1172   for (l = bookmarks; l; l = l->next)
1173     {
1174       const char *bookmark;
1175
1176       bookmark = l->data;
1177       if (strcmp (bookmark, uri) == 0)
1178         {
1179           g_free (l->data);
1180           bookmarks = g_slist_remove_link (bookmarks, l);
1181           g_slist_free_1 (l);
1182
1183           if (bookmark_list_write (bookmarks, error))
1184             {
1185               result = TRUE;
1186               g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1187             }
1188
1189           goto out;
1190         }
1191     }
1192
1193   g_set_error (error,
1194                GTK_FILE_SYSTEM_ERROR,
1195                GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1196                "%s does not exist in the bookmarks list",
1197                uri);
1198
1199  out:
1200
1201   g_free (uri);
1202   bookmark_list_free (bookmarks);
1203
1204   return result;
1205 }
1206
1207 static GSList *
1208 gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
1209 {
1210   GSList *bookmarks;
1211   GSList *result;
1212   GSList *l;
1213
1214   if (!bookmark_list_read (&bookmarks, NULL))
1215     return NULL;
1216
1217   result = NULL;
1218
1219   for (l = bookmarks; l; l = l->next)
1220     {
1221       const char *name;
1222
1223       name = l->data;
1224
1225       if (is_local_uri (name))
1226         result = g_slist_prepend (result, gtk_file_system_unix_uri_to_path (file_system, name));
1227     }
1228
1229   bookmark_list_free (bookmarks);
1230
1231   result = g_slist_reverse (result);
1232   return result;
1233 }
1234
1235 /*
1236  * GtkFileFolderUnix
1237  */
1238 static GType
1239 gtk_file_folder_unix_get_type (void)
1240 {
1241   static GType file_folder_unix_type = 0;
1242
1243   if (!file_folder_unix_type)
1244     {
1245       static const GTypeInfo file_folder_unix_info =
1246       {
1247         sizeof (GtkFileFolderUnixClass),
1248         NULL,           /* base_init */
1249         NULL,           /* base_finalize */
1250         (GClassInitFunc) gtk_file_folder_unix_class_init,
1251         NULL,           /* class_finalize */
1252         NULL,           /* class_data */
1253         sizeof (GtkFileFolderUnix),
1254         0,              /* n_preallocs */
1255         (GInstanceInitFunc) gtk_file_folder_unix_init,
1256       };
1257
1258       static const GInterfaceInfo file_folder_info =
1259       {
1260         (GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
1261         NULL,                                                 /* interface_finalize */
1262         NULL                                                  /* interface_data */
1263       };
1264
1265       file_folder_unix_type = g_type_register_static (G_TYPE_OBJECT,
1266                                                       "GtkFileFolderUnix",
1267                                                       &file_folder_unix_info, 0);
1268       g_type_add_interface_static (file_folder_unix_type,
1269                                    GTK_TYPE_FILE_FOLDER,
1270                                    &file_folder_info);
1271     }
1272
1273   return file_folder_unix_type;
1274 }
1275
1276 static void
1277 gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
1278 {
1279   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1280
1281   folder_parent_class = g_type_class_peek_parent (class);
1282
1283   gobject_class->finalize = gtk_file_folder_unix_finalize;
1284 }
1285
1286 static void
1287 gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
1288 {
1289   iface->get_info = gtk_file_folder_unix_get_info;
1290   iface->list_children = gtk_file_folder_unix_list_children;
1291 }
1292
1293 static void
1294 gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
1295 {
1296 }
1297
1298 static void
1299 gtk_file_folder_unix_finalize (GObject *object)
1300 {
1301   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
1302
1303   g_hash_table_remove (folder_unix->system_unix->folder_hash, folder_unix->filename);
1304
1305   g_free (folder_unix->filename);
1306
1307   folder_parent_class->finalize (object);
1308 }
1309
1310 static GtkFileInfo *
1311 gtk_file_folder_unix_get_info (GtkFileFolder  *folder,
1312                                const GtkFilePath    *path,
1313                                GError        **error)
1314 {
1315   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1316   GtkFileInfo *info;
1317   gchar *dirname;
1318   const char *filename;
1319
1320   filename = gtk_file_path_get_string (path);
1321   g_return_val_if_fail (filename != NULL, NULL);
1322   g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1323
1324   dirname = g_path_get_dirname (filename);
1325   g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
1326   g_free (dirname);
1327
1328   info = filename_get_info (filename, folder_unix->types, error);
1329
1330   return info;
1331 }
1332
1333 static gboolean
1334 gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
1335                                     GSList        **children,
1336                                     GError        **error)
1337 {
1338   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1339   GError *tmp_error = NULL;
1340   GDir *dir;
1341
1342   *children = NULL;
1343
1344   dir = g_dir_open (folder_unix->filename, 0, &tmp_error);
1345   if (!dir)
1346     {
1347       g_set_error (error,
1348                    GTK_FILE_SYSTEM_ERROR,
1349                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1350                    "%s",
1351                    tmp_error->message);
1352
1353       g_error_free (tmp_error);
1354
1355       return FALSE;
1356     }
1357
1358   while (TRUE)
1359     {
1360       const gchar *filename = g_dir_read_name (dir);
1361       gchar *fullname;
1362
1363       if (!filename)
1364         break;
1365
1366       fullname = g_build_filename (folder_unix->filename, filename, NULL);
1367       *children = g_slist_prepend (*children, filename_to_path (fullname));
1368       g_free (fullname);
1369     }
1370
1371   g_dir_close (dir);
1372
1373   *children = g_slist_reverse (*children);
1374
1375   return TRUE;
1376 }
1377
1378 static GtkFileInfo *
1379 filename_get_info (const gchar     *filename,
1380                    GtkFileInfoType  types,
1381                    GError         **error)
1382 {
1383   GtkFileInfo *info;
1384   struct stat statbuf;
1385   gboolean do_stat = (types & (GTK_FILE_INFO_IS_FOLDER |
1386                                GTK_FILE_INFO_IS_HIDDEN |
1387                                GTK_FILE_INFO_MODIFICATION_TIME |
1388                                GTK_FILE_INFO_SIZE));
1389
1390   /* If stat fails, try to fall back to lstat to catch broken links
1391    */
1392   if (do_stat && stat (filename, &statbuf) != 0)
1393     {
1394       if (errno != ENOENT || lstat (filename, &statbuf) != 0)
1395         {
1396           int save_errno = errno;
1397           gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1398           g_set_error (error,
1399                        GTK_FILE_SYSTEM_ERROR,
1400                        GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1401                        _("error getting information for '%s': %s"),
1402                        filename_utf8 ? filename_utf8 : "???",
1403                        g_strerror (save_errno));
1404           g_free (filename_utf8);
1405
1406           return NULL;
1407         }
1408     }
1409
1410   info = gtk_file_info_new ();
1411
1412   if (filename_is_root (filename))
1413     {
1414       if (types & GTK_FILE_INFO_DISPLAY_NAME)
1415         gtk_file_info_set_display_name (info, "/");
1416
1417       if (types & GTK_FILE_INFO_IS_HIDDEN)
1418         gtk_file_info_set_is_hidden (info, FALSE);
1419     }
1420   else
1421     {
1422       gchar *basename = g_path_get_basename (filename);
1423
1424       if (types & GTK_FILE_INFO_DISPLAY_NAME)
1425         {
1426           gchar *display_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1427           if (!display_name)
1428             display_name = g_strescape (basename, NULL);
1429
1430           gtk_file_info_set_display_name (info, display_name);
1431
1432           g_free (display_name);
1433         }
1434
1435       if (types & GTK_FILE_INFO_IS_HIDDEN)
1436         {
1437           gtk_file_info_set_is_hidden (info, basename[0] == '.');
1438         }
1439
1440       g_free (basename);
1441     }
1442
1443   if (types & GTK_FILE_INFO_IS_FOLDER)
1444     {
1445       gtk_file_info_set_is_folder (info, S_ISDIR (statbuf.st_mode));
1446    }
1447
1448   if (types & GTK_FILE_INFO_MIME_TYPE)
1449     {
1450       const char *mime_type = xdg_mime_get_mime_type_for_file (filename);
1451       gtk_file_info_set_mime_type (info, mime_type);
1452     }
1453
1454   if (types & GTK_FILE_INFO_MODIFICATION_TIME)
1455     {
1456       gtk_file_info_set_modification_time (info, statbuf.st_mtime);
1457     }
1458
1459   if (types & GTK_FILE_INFO_SIZE)
1460     {
1461       gtk_file_info_set_size (info, (gint64)statbuf.st_size);
1462     }
1463
1464   return info;
1465 }
1466
1467 static GtkFilePath *
1468 filename_to_path (const char *filename)
1469 {
1470   return gtk_file_path_new_dup (filename);
1471 }
1472
1473 static gboolean
1474 filename_is_root (const char *filename)
1475 {
1476   const gchar *after_root;
1477
1478   after_root = g_path_skip_root (filename);
1479
1480   return (after_root != NULL && *after_root == '\0');
1481 }