]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemunix.c
Fixes #136082 and #135265, patch by Morten Welinder.
[~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       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
382       g_set_error (error,
383                    GTK_FILE_SYSTEM_ERROR,
384                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
385                    _("error creating directory '%s': %s"),
386                    filename_utf8 ? filename_utf8 : "???",
387                    g_strerror (errno));
388       g_free (filename_utf8);
389       return FALSE;
390     }
391
392   if (filename_is_root (filename))
393     return TRUE; /* hmmm, but with no notification */
394
395   parent = g_path_get_dirname (filename);
396   if (parent)
397     {
398       GtkFileFolderUnix *folder_unix;
399
400       folder_unix = g_hash_table_lookup (system_unix->folder_hash, parent);
401       if (folder_unix)
402         {
403           GSList *paths;
404
405           paths = g_slist_append (NULL, (GtkFilePath *) path);
406           g_signal_emit_by_name (folder_unix, "files-added", paths);
407           g_slist_free (paths);
408         }
409     }
410
411   return TRUE;
412 }
413
414 static void
415 gtk_file_system_unix_volume_free (GtkFileSystem        *file_system,
416                                   GtkFileSystemVolume  *volume)
417 {
418   GtkFilePath *path;
419
420   path = (GtkFilePath *) volume;
421   gtk_file_path_free (path);
422 }
423
424 static GtkFilePath *
425 gtk_file_system_unix_volume_get_base_path (GtkFileSystem        *file_system,
426                                            GtkFileSystemVolume  *volume)
427 {
428   return gtk_file_path_new_dup ("/");
429 }
430
431 static gboolean
432 gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem        *file_system,
433                                             GtkFileSystemVolume  *volume)
434 {
435   return TRUE;
436 }
437
438 static gboolean
439 gtk_file_system_unix_volume_mount (GtkFileSystem        *file_system,
440                                    GtkFileSystemVolume  *volume,
441                                    GError              **error)
442 {
443   g_set_error (error,
444                GTK_FILE_SYSTEM_ERROR,
445                GTK_FILE_SYSTEM_ERROR_FAILED,
446                _("This file system does not support mounting"));
447   return FALSE;
448 }
449
450 static gchar *
451 gtk_file_system_unix_volume_get_display_name (GtkFileSystem       *file_system,
452                                               GtkFileSystemVolume *volume)
453 {
454   return g_strdup (_("Filesystem")); /* Same as Nautilus */
455 }
456
457 static IconType
458 get_icon_type (const char *filename,
459                GError    **error)
460 {
461   struct stat statbuf;
462   IconType icon_type;
463
464   /* If stat fails, try to fall back to lstat to catch broken links
465    */
466   if (stat (filename, &statbuf) != 0 &&
467       lstat (filename, &statbuf) != 0)
468     {
469       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
470       g_set_error (error,
471                    GTK_FILE_SYSTEM_ERROR,
472                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
473                    _("error getting information for '%s': %s"),
474                    filename_utf8 ? filename_utf8 : "???",
475                    g_strerror (errno));
476       g_free (filename_utf8);
477
478       return ICON_NONE;
479     }
480
481   if (S_ISBLK (statbuf.st_mode))
482     icon_type = ICON_BLOCK_DEVICE;
483   else if (S_ISLNK (statbuf.st_mode))
484     icon_type = ICON_BROKEN_SYMBOLIC_LINK; /* See above */
485   else if (S_ISCHR (statbuf.st_mode))
486     icon_type = ICON_CHARACTER_DEVICE;
487   else if (S_ISDIR (statbuf.st_mode))
488     icon_type = ICON_DIRECTORY;
489   else if (S_ISFIFO (statbuf.st_mode))
490     icon_type =  ICON_FIFO;
491   else if (S_ISSOCK (statbuf.st_mode))
492     icon_type = ICON_SOCKET;
493   else
494     {
495       icon_type = ICON_REGULAR;
496
497 #if 0
498       if ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR &&
499           (statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) &&
500           (strcmp (mime_type, XDG_MIME_TYPE_UNKNOWN) == 0 ||
501            strcmp (mime_type, "application/x-executable") == 0 ||
502            strcmp (mime_type, "application/x-shellscript") == 0))
503         gtk_file_info_set_icon_type (info, GTK_FILE_ICON_EXECUTABLE);
504 #endif
505     }
506
507   return icon_type;
508 }
509
510 typedef struct
511 {
512   gint size;
513   GdkPixbuf *pixbuf;
514 } IconCacheElement;
515
516 static void
517 icon_cache_element_free (IconCacheElement *element)
518 {
519   if (element->pixbuf)
520     g_object_unref (element->pixbuf);
521   g_free (element);
522 }
523
524 static void
525 icon_theme_changed (GtkIconTheme *icon_theme)
526 {
527   GHashTable *cache;
528
529   /* Difference from the initial creation is that we don't
530    * reconnect the signal
531    */
532   cache = g_hash_table_new_full (g_str_hash, g_str_equal,
533                                  (GDestroyNotify)g_free,
534                                  (GDestroyNotify)icon_cache_element_free);
535   g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
536                           cache, (GDestroyNotify)g_hash_table_destroy);
537 }
538
539 static GdkPixbuf *
540 get_cached_icon (GtkWidget   *widget,
541                  const gchar *name,
542                  gint         pixel_size)
543 {
544   GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
545   GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
546   IconCacheElement *element;
547
548   if (!cache)
549     {
550       cache = g_hash_table_new_full (g_str_hash, g_str_equal,
551                                      (GDestroyNotify)g_free,
552                                      (GDestroyNotify)icon_cache_element_free);
553
554       g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
555                               cache, (GDestroyNotify)g_hash_table_destroy);
556       g_signal_connect (icon_theme, "changed",
557                         G_CALLBACK (icon_theme_changed), NULL);
558     }
559
560   element = g_hash_table_lookup (cache, name);
561   if (!element)
562     {
563       element = g_new0 (IconCacheElement, 1);
564       g_hash_table_insert (cache, g_strdup (name), element);
565     }
566
567   if (element->size != pixel_size)
568     {
569       if (element->pixbuf)
570         g_object_unref (element->pixbuf);
571       element->size = pixel_size;
572       element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
573                                                   pixel_size, 0, NULL);
574     }
575
576   return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
577 }
578
579 static GdkPixbuf *
580 gtk_file_system_unix_volume_render_icon (GtkFileSystem        *file_system,
581                                          GtkFileSystemVolume  *volume,
582                                          GtkWidget            *widget,
583                                          gint                  pixel_size,
584                                          GError              **error)
585 {
586   /* FIXME: set the GError if we can't load the icon */
587   return get_cached_icon (widget, "gnome-fs-blockdev", pixel_size);
588 }
589
590 static gboolean
591 gtk_file_system_unix_get_parent (GtkFileSystem     *file_system,
592                                  const GtkFilePath *path,
593                                  GtkFilePath      **parent,
594                                  GError           **error)
595 {
596   const char *filename;
597
598   filename = gtk_file_path_get_string (path);
599   g_return_val_if_fail (filename != NULL, FALSE);
600   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
601
602   if (filename_is_root (filename))
603     {
604       *parent = NULL;
605     }
606   else
607     {
608       gchar *parent_filename = g_path_get_dirname (filename);
609       *parent = filename_to_path (parent_filename);
610       g_free (parent_filename);
611     }
612
613   return TRUE;
614 }
615
616 static GtkFilePath *
617 gtk_file_system_unix_make_path (GtkFileSystem    *file_system,
618                                const GtkFilePath *base_path,
619                                const gchar       *display_name,
620                                GError           **error)
621 {
622   const char *base_filename;
623   gchar *filename;
624   gchar *full_filename;
625   GError *tmp_error = NULL;
626   GtkFilePath *result;
627
628   base_filename = gtk_file_path_get_string (base_path);
629   g_return_val_if_fail (base_filename != NULL, NULL);
630   g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
631
632   filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
633   if (!filename)
634     {
635       g_set_error (error,
636                    GTK_FILE_SYSTEM_ERROR,
637                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
638                    "%s",
639                    tmp_error->message);
640
641       g_error_free (tmp_error);
642
643       return NULL;
644     }
645
646   full_filename = g_build_filename (base_filename, filename, NULL);
647   result = filename_to_path (full_filename);
648   g_free (filename);
649   g_free (full_filename);
650
651   return result;
652 }
653
654 /* If this was a publically exported function, it should return
655  * a dup'ed result, but we make it modify-in-place for efficiency
656  * here, and because it works for us.
657  */
658 static void
659 canonicalize_filename (gchar *filename)
660 {
661   gchar *p, *q;
662   gboolean last_was_slash = FALSE;
663
664   p = filename;
665   q = filename;
666
667   while (*p)
668     {
669       if (*p == G_DIR_SEPARATOR)
670         {
671           if (!last_was_slash)
672             *q++ = G_DIR_SEPARATOR;
673
674           last_was_slash = TRUE;
675         }
676       else
677         {
678           if (last_was_slash && *p == '.')
679             {
680               if (*(p + 1) == G_DIR_SEPARATOR ||
681                   *(p + 1) == '\0')
682                 {
683                   if (*(p + 1) == '\0')
684                     break;
685
686                   p += 1;
687                 }
688               else if (*(p + 1) == '.' &&
689                        (*(p + 2) == G_DIR_SEPARATOR ||
690                         *(p + 2) == '\0'))
691                 {
692                   if (q > filename + 1)
693                     {
694                       q--;
695                       while (q > filename + 1 &&
696                              *(q - 1) != G_DIR_SEPARATOR)
697                         q--;
698                     }
699
700                   if (*(p + 2) == '\0')
701                     break;
702
703                   p += 2;
704                 }
705               else
706                 {
707                   *q++ = *p;
708                   last_was_slash = FALSE;
709                 }
710             }
711           else
712             {
713               *q++ = *p;
714               last_was_slash = FALSE;
715             }
716         }
717
718       p++;
719     }
720
721   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
722     q--;
723
724   *q = '\0';
725 }
726
727 static gboolean
728 gtk_file_system_unix_parse (GtkFileSystem     *file_system,
729                             const GtkFilePath *base_path,
730                             const gchar       *str,
731                             GtkFilePath      **folder,
732                             gchar            **file_part,
733                             GError           **error)
734 {
735   const char *base_filename;
736   gchar *last_slash;
737   gboolean result = FALSE;
738
739   base_filename = gtk_file_path_get_string (base_path);
740   g_return_val_if_fail (base_filename != NULL, FALSE);
741   g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
742
743   last_slash = strrchr (str, G_DIR_SEPARATOR);
744   if (!last_slash)
745     {
746       *folder = gtk_file_path_copy (base_path);
747       *file_part = g_strdup (str);
748       result = TRUE;
749     }
750   else
751     {
752       gchar *folder_part;
753       gchar *folder_path;
754       GError *tmp_error = NULL;
755
756       if (last_slash == str)
757         folder_part = g_strdup ("/");
758       else
759         folder_part = g_filename_from_utf8 (str, last_slash - str,
760                                             NULL, NULL, &tmp_error);
761
762       if (!folder_part)
763         {
764           g_set_error (error,
765                        GTK_FILE_SYSTEM_ERROR,
766                        GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
767                        "%s",
768                        tmp_error->message);
769           g_error_free (tmp_error);
770         }
771       else
772         {
773           if (folder_part[0] == G_DIR_SEPARATOR)
774             folder_path = folder_part;
775           else
776             {
777               folder_path = g_build_filename (base_filename, folder_part, NULL);
778               g_free (folder_part);
779             }
780
781           canonicalize_filename (folder_path);
782
783           *folder = filename_to_path (folder_path);
784           *file_part = g_strdup (last_slash + 1);
785
786           g_free (folder_path);
787
788           result = TRUE;
789         }
790     }
791
792   return result;
793 }
794
795 static gchar *
796 gtk_file_system_unix_path_to_uri (GtkFileSystem     *file_system,
797                                   const GtkFilePath *path)
798 {
799   return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
800 }
801
802 static gchar *
803 gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
804                                        const GtkFilePath *path)
805 {
806   return g_strdup (gtk_file_path_get_string (path));
807 }
808
809 static GtkFilePath *
810 gtk_file_system_unix_uri_to_path (GtkFileSystem     *file_system,
811                                   const gchar       *uri)
812 {
813   gchar *filename = g_filename_from_uri (uri, NULL, NULL);
814   if (filename)
815     return gtk_file_path_new_steal (filename);
816   else
817     return NULL;
818 }
819
820 static GtkFilePath *
821 gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
822                                        const gchar   *filename)
823 {
824   return gtk_file_path_new_dup (filename);
825 }
826
827 static GdkPixbuf *
828 gtk_file_system_unix_render_icon (GtkFileSystem     *file_system,
829                                   const GtkFilePath *path,
830                                   GtkWidget         *widget,
831                                   gint               pixel_size,
832                                   GError           **error)
833 {
834   const char *filename;
835   IconType icon_type;
836   const char *mime_type;
837
838   filename = gtk_file_path_get_string (path);
839   icon_type = get_icon_type (filename, error);
840
841   /* FIXME: this function should not return NULL without setting the GError; we
842    * should perhaps provide a "never fails" generic stock icon for when all else
843    * fails.
844    */
845
846   if (icon_type == ICON_NONE)
847     return NULL;
848
849   if (icon_type != ICON_REGULAR)
850     {
851       const char *name;
852
853       switch (icon_type)
854         {
855         case ICON_BLOCK_DEVICE:
856           name = "gnome-fs-blockdev";
857           break;
858         case ICON_BROKEN_SYMBOLIC_LINK:
859           name = "gnome-fs-symlink";
860           break;
861         case ICON_CHARACTER_DEVICE:
862           name = "gnome-fs-chardev";
863           break;
864         case ICON_DIRECTORY:
865           name = "gnome-fs-directory";
866           break;
867         case ICON_EXECUTABLE:
868           name ="gnome-fs-executable";
869           break;
870         case ICON_FIFO:
871           name = "gnome-fs-fifo";
872           break;
873         case ICON_SOCKET:
874           name = "gnome-fs-socket";
875           break;
876         default:
877           g_assert_not_reached ();
878           return NULL;
879         }
880
881       return get_cached_icon (widget, name, pixel_size);
882     }
883
884   mime_type = xdg_mime_get_mime_type_for_file (filename);
885   if (mime_type)
886     {
887       const char *separator;
888       GString *icon_name;
889       GdkPixbuf *pixbuf;
890
891       separator = strchr (mime_type, '/');
892       if (!separator)
893         return NULL;
894
895       icon_name = g_string_new ("gnome-mime-");
896       g_string_append_len (icon_name, mime_type, separator - mime_type);
897       g_string_append_c (icon_name, '-');
898       g_string_append (icon_name, separator + 1);
899       pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
900       g_string_free (icon_name, TRUE);
901       if (pixbuf)
902         return pixbuf;
903
904       icon_name = g_string_new ("gnome-mime-");
905       g_string_append_len (icon_name, mime_type, separator - mime_type);
906       pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
907       g_string_free (icon_name, TRUE);
908       if (pixbuf)
909         return pixbuf;
910     }
911
912   return get_cached_icon (widget, "gnome-fs-regular", pixel_size);
913 }
914
915 static void
916 bookmark_list_free (GSList *list)
917 {
918   GSList *l;
919
920   for (l = list; l; l = l->next)
921     g_free (l->data);
922
923   g_slist_free (list);
924 }
925
926 /* Returns whether a URI is a local file:// */
927 static gboolean
928 is_local_uri (const char *uri)
929 {
930   char *filename;
931   char *hostname;
932   gboolean result;
933
934   /* This is rather crude, but hey */
935   filename = g_filename_from_uri (uri, &hostname, NULL);
936
937   result = (filename && !hostname);
938
939   g_free (filename);
940   g_free (hostname);
941
942   return result;
943 }
944
945 static char *
946 bookmark_get_filename (gboolean tmp_file)
947 {
948   char *filename;
949
950   filename = g_build_filename (g_get_home_dir (),
951                                tmp_file ? BOOKMARKS_TMP_FILENAME : BOOKMARKS_FILENAME,
952                                NULL);
953   g_assert (filename != NULL);
954   return filename;
955 }
956
957 static gboolean
958 bookmark_list_read (GSList **bookmarks, GError **error)
959 {
960   gchar *filename;
961   gchar *contents;
962   gboolean result = FALSE;
963
964   filename = bookmark_get_filename (FALSE);
965   *bookmarks = NULL;
966
967   if (g_file_get_contents (filename, &contents, NULL, error))
968     {
969       gchar **lines = g_strsplit (contents, "\n", -1);
970       int i;
971       GHashTable *table;
972
973       table = g_hash_table_new (g_str_hash, g_str_equal);
974
975       for (i = 0; lines[i]; i++)
976         {
977           if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
978             {
979               *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
980               g_hash_table_insert (table, lines[i], lines[i]);
981             }
982         }
983
984       g_free (contents);
985       g_hash_table_destroy (table);
986       g_strfreev (lines);
987
988       *bookmarks = g_slist_reverse (*bookmarks);
989       result = TRUE;
990     }
991
992   g_free (filename);
993
994   return result;
995 }
996
997 static gboolean
998 bookmark_list_write (GSList *bookmarks, GError **error)
999 {
1000   char *tmp_filename;
1001   char *filename;
1002   gboolean result = TRUE;
1003   FILE *file;
1004   int fd;
1005   int saved_errno;
1006
1007   /* First, write a temporary file */
1008
1009   tmp_filename = bookmark_get_filename (TRUE);
1010   filename = bookmark_get_filename (FALSE);
1011
1012   fd = g_mkstemp (tmp_filename);
1013   if (fd == -1)
1014     {
1015       saved_errno = errno;
1016       goto io_error;
1017     }
1018
1019   if ((file = fdopen (fd, "w")) != NULL)
1020     {
1021       GSList *l;
1022
1023       for (l = bookmarks; l; l = l->next)
1024         if (fputs (l->data, file) == EOF
1025             || fputs ("\n", file) == EOF)
1026           {
1027             saved_errno = errno;
1028             goto io_error;
1029           }
1030
1031       if (fclose (file) == EOF)
1032         {
1033           saved_errno = errno;
1034           goto io_error;
1035         }
1036
1037       if (rename (tmp_filename, filename) == -1)
1038         {
1039           saved_errno = errno;
1040           goto io_error;
1041         }
1042
1043       result = TRUE;
1044       goto out;
1045     }
1046   else
1047     {
1048       saved_errno = errno;
1049
1050       /* fdopen() failed, so we can't do much error checking here anyway */
1051       close (fd);
1052     }
1053
1054  io_error:
1055
1056   g_set_error (error,
1057                GTK_FILE_SYSTEM_ERROR,
1058                GTK_FILE_SYSTEM_ERROR_FAILED,
1059                _("Bookmark saving failed (%s)"),
1060                g_strerror (saved_errno));
1061   result = FALSE;
1062
1063   if (fd != -1)
1064     unlink (tmp_filename); /* again, not much error checking we can do here */
1065
1066  out:
1067
1068   g_free (filename);
1069   g_free (tmp_filename);
1070
1071   return result;
1072 }
1073
1074 static gboolean
1075 gtk_file_system_unix_insert_bookmark (GtkFileSystem     *file_system,
1076                                       const GtkFilePath *path,
1077                                       gint               position,
1078                                       GError           **error)
1079 {
1080   GSList *bookmarks;
1081   int num_bookmarks;
1082   GSList *l;
1083   char *uri;
1084   gboolean result;
1085   GError *err;
1086
1087   err = NULL;
1088   if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
1089     {
1090       g_propagate_error (error, err);
1091       return FALSE;
1092     }
1093
1094   num_bookmarks = g_slist_length (bookmarks);
1095   g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
1096
1097   result = FALSE;
1098
1099   uri = gtk_file_system_unix_path_to_uri (file_system, path);
1100
1101   for (l = bookmarks; l; l = l->next)
1102     {
1103       const char *bookmark;
1104
1105       bookmark = l->data;
1106       if (strcmp (bookmark, uri) == 0)
1107         {
1108           g_set_error (error,
1109                        GTK_FILE_SYSTEM_ERROR,
1110                        GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
1111                        "%s already exists in the bookmarks list",
1112                        uri);
1113           goto out;
1114         }
1115     }
1116
1117   bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
1118   if (bookmark_list_write (bookmarks, error))
1119     {
1120       result = TRUE;
1121       g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1122     }
1123
1124  out:
1125
1126   g_free (uri);
1127   bookmark_list_free (bookmarks);
1128
1129   return result;
1130 }
1131
1132 static gboolean
1133 gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
1134                                       const GtkFilePath *path,
1135                                       GError           **error)
1136 {
1137   GSList *bookmarks;
1138   char *uri;
1139   GSList *l;
1140   gboolean result;
1141
1142   if (!bookmark_list_read (&bookmarks, error))
1143     return FALSE;
1144
1145   result = FALSE;
1146
1147   uri = gtk_file_system_path_to_uri (file_system, path);
1148
1149   for (l = bookmarks; l; l = l->next)
1150     {
1151       const char *bookmark;
1152
1153       bookmark = l->data;
1154       if (strcmp (bookmark, uri) == 0)
1155         {
1156           g_free (l->data);
1157           bookmarks = g_slist_remove_link (bookmarks, l);
1158           g_slist_free_1 (l);
1159
1160           if (bookmark_list_write (bookmarks, error))
1161             {
1162               result = TRUE;
1163               g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1164             }
1165
1166           goto out;
1167         }
1168     }
1169
1170   g_set_error (error,
1171                GTK_FILE_SYSTEM_ERROR,
1172                GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1173                "%s does not exist in the bookmarks list",
1174                uri);
1175
1176  out:
1177
1178   g_free (uri);
1179   bookmark_list_free (bookmarks);
1180
1181   return result;
1182 }
1183
1184 static GSList *
1185 gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
1186 {
1187   GSList *bookmarks;
1188   GSList *result;
1189   GSList *l;
1190
1191   if (!bookmark_list_read (&bookmarks, NULL))
1192     return NULL;
1193
1194   result = NULL;
1195
1196   for (l = bookmarks; l; l = l->next)
1197     {
1198       const char *name;
1199
1200       name = l->data;
1201
1202       if (is_local_uri (name))
1203         result = g_slist_prepend (result, gtk_file_system_unix_uri_to_path (file_system, name));
1204     }
1205
1206   bookmark_list_free (bookmarks);
1207
1208   result = g_slist_reverse (result);
1209   return result;
1210 }
1211
1212 /*
1213  * GtkFileFolderUnix
1214  */
1215 static GType
1216 gtk_file_folder_unix_get_type (void)
1217 {
1218   static GType file_folder_unix_type = 0;
1219
1220   if (!file_folder_unix_type)
1221     {
1222       static const GTypeInfo file_folder_unix_info =
1223       {
1224         sizeof (GtkFileFolderUnixClass),
1225         NULL,           /* base_init */
1226         NULL,           /* base_finalize */
1227         (GClassInitFunc) gtk_file_folder_unix_class_init,
1228         NULL,           /* class_finalize */
1229         NULL,           /* class_data */
1230         sizeof (GtkFileFolderUnix),
1231         0,              /* n_preallocs */
1232         (GInstanceInitFunc) gtk_file_folder_unix_init,
1233       };
1234
1235       static const GInterfaceInfo file_folder_info =
1236       {
1237         (GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
1238         NULL,                                                 /* interface_finalize */
1239         NULL                                                  /* interface_data */
1240       };
1241
1242       file_folder_unix_type = g_type_register_static (G_TYPE_OBJECT,
1243                                                       "GtkFileFolderUnix",
1244                                                       &file_folder_unix_info, 0);
1245       g_type_add_interface_static (file_folder_unix_type,
1246                                    GTK_TYPE_FILE_FOLDER,
1247                                    &file_folder_info);
1248     }
1249
1250   return file_folder_unix_type;
1251 }
1252
1253 static void
1254 gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
1255 {
1256   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1257
1258   folder_parent_class = g_type_class_peek_parent (class);
1259
1260   gobject_class->finalize = gtk_file_folder_unix_finalize;
1261 }
1262
1263 static void
1264 gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
1265 {
1266   iface->get_info = gtk_file_folder_unix_get_info;
1267   iface->list_children = gtk_file_folder_unix_list_children;
1268 }
1269
1270 static void
1271 gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
1272 {
1273 }
1274
1275 static void
1276 gtk_file_folder_unix_finalize (GObject *object)
1277 {
1278   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
1279
1280   g_hash_table_remove (folder_unix->system_unix->folder_hash, folder_unix->filename);
1281
1282   g_free (folder_unix->filename);
1283
1284   folder_parent_class->finalize (object);
1285 }
1286
1287 static GtkFileInfo *
1288 gtk_file_folder_unix_get_info (GtkFileFolder  *folder,
1289                                const GtkFilePath    *path,
1290                                GError        **error)
1291 {
1292   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1293   GtkFileInfo *info;
1294   gchar *dirname;
1295   const char *filename;
1296
1297   filename = gtk_file_path_get_string (path);
1298   g_return_val_if_fail (filename != NULL, NULL);
1299   g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1300
1301   dirname = g_path_get_dirname (filename);
1302   g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
1303   g_free (dirname);
1304
1305   info = filename_get_info (filename, folder_unix->types, error);
1306
1307   return info;
1308 }
1309
1310 static gboolean
1311 gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
1312                                     GSList        **children,
1313                                     GError        **error)
1314 {
1315   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1316   GError *tmp_error = NULL;
1317   GDir *dir;
1318
1319   *children = NULL;
1320
1321   dir = g_dir_open (folder_unix->filename, 0, &tmp_error);
1322   if (!dir)
1323     {
1324       g_set_error (error,
1325                    GTK_FILE_SYSTEM_ERROR,
1326                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1327                    "%s",
1328                    tmp_error->message);
1329
1330       g_error_free (tmp_error);
1331
1332       return FALSE;
1333     }
1334
1335   while (TRUE)
1336     {
1337       const gchar *filename = g_dir_read_name (dir);
1338       gchar *fullname;
1339
1340       if (!filename)
1341         break;
1342
1343       fullname = g_build_filename (folder_unix->filename, filename, NULL);
1344       *children = g_slist_prepend (*children, filename_to_path (fullname));
1345       g_free (fullname);
1346     }
1347
1348   g_dir_close (dir);
1349
1350   *children = g_slist_reverse (*children);
1351
1352   return TRUE;
1353 }
1354
1355 static GtkFileInfo *
1356 filename_get_info (const gchar     *filename,
1357                    GtkFileInfoType  types,
1358                    GError         **error)
1359 {
1360   GtkFileInfo *info;
1361   struct stat statbuf;
1362   gboolean do_stat = (types & (GTK_FILE_INFO_IS_FOLDER |
1363                                GTK_FILE_INFO_IS_HIDDEN |
1364                                GTK_FILE_INFO_MODIFICATION_TIME |
1365                                GTK_FILE_INFO_SIZE));
1366
1367   /* If stat fails, try to fall back to lstat to catch broken links
1368    */
1369   if (do_stat &&
1370       stat (filename, &statbuf) != 0 &&
1371       lstat (filename, &statbuf) != 0)
1372     {
1373       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1374       g_set_error (error,
1375                    GTK_FILE_SYSTEM_ERROR,
1376                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1377                    _("error getting information for '%s': %s"),
1378                    filename_utf8 ? filename_utf8 : "???",
1379                    g_strerror (errno));
1380       g_free (filename_utf8);
1381
1382       return NULL;
1383     }
1384
1385   info = gtk_file_info_new ();
1386
1387   if (filename_is_root (filename))
1388     {
1389       if (types & GTK_FILE_INFO_DISPLAY_NAME)
1390         gtk_file_info_set_display_name (info, "/");
1391
1392       if (types & GTK_FILE_INFO_IS_HIDDEN)
1393         gtk_file_info_set_is_hidden (info, FALSE);
1394     }
1395   else
1396     {
1397       gchar *basename = g_path_get_basename (filename);
1398
1399       if (types & GTK_FILE_INFO_DISPLAY_NAME)
1400         {
1401           gchar *display_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1402           if (!display_name)
1403             display_name = g_strescape (basename, NULL);
1404
1405           gtk_file_info_set_display_name (info, display_name);
1406
1407           g_free (display_name);
1408         }
1409
1410       if (types & GTK_FILE_INFO_IS_HIDDEN)
1411         {
1412           gtk_file_info_set_is_hidden (info, basename[0] == '.');
1413         }
1414
1415       g_free (basename);
1416     }
1417
1418   if (types & GTK_FILE_INFO_IS_FOLDER)
1419     {
1420       gtk_file_info_set_is_folder (info, S_ISDIR (statbuf.st_mode));
1421    }
1422
1423   if (types & GTK_FILE_INFO_MIME_TYPE)
1424     {
1425       const char *mime_type = xdg_mime_get_mime_type_for_file (filename);
1426       gtk_file_info_set_mime_type (info, mime_type);
1427     }
1428
1429   if (types & GTK_FILE_INFO_MODIFICATION_TIME)
1430     {
1431       gtk_file_info_set_modification_time (info, statbuf.st_mtime);
1432     }
1433
1434   if (types & GTK_FILE_INFO_SIZE)
1435     {
1436       gtk_file_info_set_size (info, (gint64)statbuf.st_size);
1437     }
1438
1439   return info;
1440 }
1441
1442 static GtkFilePath *
1443 filename_to_path (const char *filename)
1444 {
1445   return gtk_file_path_new_dup (filename);
1446 }
1447
1448 static gboolean
1449 filename_is_root (const char *filename)
1450 {
1451   const gchar *after_root;
1452
1453   after_root = g_path_skip_root (filename);
1454
1455   return (after_root != NULL && *after_root == '\0');
1456 }