]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemunix.c
g_path_skip_root() can return NULL; handle this. Fixes #129565.
[~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 "gtkfilesystem.h"
22 #include "gtkfilesystemunix.h"
23 #include "gtkintl.h"
24
25 #define XDG_PREFIX _gtk_xdg
26 #include "xdgmime/xdgmime.h"
27
28 #include <errno.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33
34 typedef struct _GtkFileSystemUnixClass GtkFileSystemUnixClass;
35
36 #define GTK_FILE_SYSTEM_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
37 #define GTK_IS_FILE_SYSTEM_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_UNIX))
38 #define GTK_FILE_SYSTEM_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
39
40 struct _GtkFileSystemUnixClass
41 {
42   GObjectClass parent_class;
43 };
44
45 struct _GtkFileSystemUnix
46 {
47   GObject parent_instance;
48 };
49
50 /* Simple stub for our returned volumes */
51 typedef struct {
52   int dummy;
53 } Volume;
54
55 #define GTK_TYPE_FILE_FOLDER_UNIX             (gtk_file_folder_unix_get_type ())
56 #define GTK_FILE_FOLDER_UNIX(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnix))
57 #define GTK_IS_FILE_FOLDER_UNIX(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_UNIX))
58 #define GTK_FILE_FOLDER_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
59 #define GTK_IS_FILE_FOLDER_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_UNIX))
60 #define GTK_FILE_FOLDER_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
61
62 typedef struct _GtkFileFolderUnix      GtkFileFolderUnix;
63 typedef struct _GtkFileFolderUnixClass GtkFileFolderUnixClass;
64
65 struct _GtkFileFolderUnixClass
66 {
67   GObjectClass parent_class;
68 };
69
70 struct _GtkFileFolderUnix
71 {
72   GObject parent_instance;
73
74   GtkFileInfoType types;
75   gchar *filename;
76 };
77
78 static GObjectClass *system_parent_class;
79 static GObjectClass *folder_parent_class;
80
81 static void gtk_file_system_unix_class_init   (GtkFileSystemUnixClass *class);
82 static void gtk_file_system_unix_iface_init   (GtkFileSystemIface     *iface);
83 static void gtk_file_system_unix_init         (GtkFileSystemUnix      *impl);
84 static void gtk_file_system_unix_finalize     (GObject                *object);
85
86 static GSList *       gtk_file_system_unix_list_volumes  (GtkFileSystem      *file_system);
87 static GSList *       gtk_file_system_unix_list_roots    (GtkFileSystem      *file_system);
88 static GtkFileInfo *  gtk_file_system_unix_get_root_info (GtkFileSystem      *file_system,
89                                                           const GtkFilePath  *path,
90                                                           GtkFileInfoType     types,
91                                                           GError            **error);
92 static GtkFileFolder *gtk_file_system_unix_get_folder    (GtkFileSystem      *file_system,
93                                                           const GtkFilePath  *path,
94                                                           GtkFileInfoType     types,
95                                                           GError            **error);
96 static gboolean       gtk_file_system_unix_create_folder (GtkFileSystem      *file_system,
97                                                           const GtkFilePath  *path,
98                                                           GError            **error);
99
100 static void         gtk_file_system_unix_volume_free             (GtkFileSystem       *file_system,
101                                                                   GtkFileSystemVolume *volume);
102 static GtkFilePath *gtk_file_system_unix_volume_get_base_path    (GtkFileSystem       *file_system,
103                                                                   GtkFileSystemVolume *volume);
104 static gboolean     gtk_file_system_unix_volume_get_is_mounted   (GtkFileSystem       *file_system,
105                                                                   GtkFileSystemVolume *volume);
106 static gboolean     gtk_file_system_unix_volume_mount            (GtkFileSystem       *file_system,
107                                                                   GtkFileSystemVolume *volume,
108                                                                   GError             **error);
109 static gchar *      gtk_file_system_unix_volume_get_display_name (GtkFileSystem       *file_system,
110                                                                   GtkFileSystemVolume *volume);
111 static GdkPixbuf *  gtk_file_system_unix_volume_render_icon      (GtkFileSystem        *file_system,
112                                                                   GtkFileSystemVolume  *volume,
113                                                                   GtkWidget            *widget,
114                                                                   gint                  pixel_size,
115                                                                   GError              **error);
116
117 static gboolean       gtk_file_system_unix_get_parent    (GtkFileSystem      *file_system,
118                                                           const GtkFilePath  *path,
119                                                           GtkFilePath       **parent,
120                                                           GError            **error);
121 static GtkFilePath *  gtk_file_system_unix_make_path     (GtkFileSystem      *file_system,
122                                                           const GtkFilePath  *base_path,
123                                                           const gchar        *display_name,
124                                                           GError            **error);
125 static gboolean       gtk_file_system_unix_parse         (GtkFileSystem      *file_system,
126                                                           const GtkFilePath  *base_path,
127                                                           const gchar        *str,
128                                                           GtkFilePath       **folder,
129                                                           gchar             **file_part,
130                                                           GError            **error);
131
132 static gchar *      gtk_file_system_unix_path_to_uri      (GtkFileSystem     *file_system,
133                                                            const GtkFilePath *path);
134 static gchar *      gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
135                                                            const GtkFilePath *path);
136 static GtkFilePath *gtk_file_system_unix_uri_to_path      (GtkFileSystem     *file_system,
137                                                            const gchar       *uri);
138 static GtkFilePath *gtk_file_system_unix_filename_to_path (GtkFileSystem     *file_system,
139                                                            const gchar       *filename);
140
141 static GdkPixbuf *gtk_file_system_unix_render_icon (GtkFileSystem     *file_system,
142                                                     const GtkFilePath *path,
143                                                     GtkWidget         *widget,
144                                                     gint               pixel_size,
145                                                     GError           **error);
146
147 static gboolean gtk_file_system_unix_add_bookmark    (GtkFileSystem     *file_system,
148                                                       const GtkFilePath *path,
149                                                       GError           **error);
150 static gboolean gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
151                                                       const GtkFilePath *path,
152                                                       GError           **error);
153 static GSList * gtk_file_system_unix_list_bookmarks  (GtkFileSystem *file_system);
154
155 static GType gtk_file_folder_unix_get_type   (void);
156 static void  gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class);
157 static void  gtk_file_folder_unix_iface_init (GtkFileFolderIface     *iface);
158 static void  gtk_file_folder_unix_init       (GtkFileFolderUnix      *impl);
159 static void  gtk_file_folder_unix_finalize   (GObject                *object);
160
161 static GtkFileInfo *gtk_file_folder_unix_get_info      (GtkFileFolder  *folder,
162                                                         const GtkFilePath    *path,
163                                                         GError        **error);
164 static gboolean     gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
165                                                         GSList        **children,
166                                                         GError        **error);
167
168 static gchar *      filename_from_path (const GtkFilePath *path);
169 static GtkFilePath *filename_to_path   (const gchar       *filename);
170
171 static gboolean     filename_is_root  (const char       *filename);
172 static GtkFileInfo *filename_get_info (const gchar      *filename,
173                                        GtkFileInfoType   types,
174                                        GError          **error);
175
176 /*
177  * GtkFileSystemUnix
178  */
179 GType
180 gtk_file_system_unix_get_type (void)
181 {
182   static GType file_system_unix_type = 0;
183
184   if (!file_system_unix_type)
185     {
186       static const GTypeInfo file_system_unix_info =
187       {
188         sizeof (GtkFileSystemUnixClass),
189         NULL,           /* base_init */
190         NULL,           /* base_finalize */
191         (GClassInitFunc) gtk_file_system_unix_class_init,
192         NULL,           /* class_finalize */
193         NULL,           /* class_data */
194         sizeof (GtkFileSystemUnix),
195         0,              /* n_preallocs */
196         (GInstanceInitFunc) gtk_file_system_unix_init,
197       };
198
199       static const GInterfaceInfo file_system_info =
200       {
201         (GInterfaceInitFunc) gtk_file_system_unix_iface_init, /* interface_init */
202         NULL,                                                 /* interface_finalize */
203         NULL                                                  /* interface_data */
204       };
205
206       file_system_unix_type = g_type_register_static (G_TYPE_OBJECT,
207                                                       "GtkFileSystemUnix",
208                                                       &file_system_unix_info, 0);
209       g_type_add_interface_static (file_system_unix_type,
210                                    GTK_TYPE_FILE_SYSTEM,
211                                    &file_system_info);
212     }
213
214   return file_system_unix_type;
215 }
216
217 /**
218  * gtk_file_system_unix_new:
219  *
220  * Creates a new #GtkFileSystemUnix object. #GtkFileSystemUnix
221  * implements the #GtkFileSystem interface with direct access to
222  * the filesystem using Unix/Linux API calls
223  *
224  * Return value: the new #GtkFileSystemUnix object
225  **/
226 GtkFileSystem *
227 gtk_file_system_unix_new (void)
228 {
229   return g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL);
230 }
231
232 static void
233 gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class)
234 {
235   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
236
237   system_parent_class = g_type_class_peek_parent (class);
238
239   gobject_class->finalize = gtk_file_system_unix_finalize;
240 }
241
242 static void
243 gtk_file_system_unix_iface_init   (GtkFileSystemIface *iface)
244 {
245   iface->list_volumes = gtk_file_system_unix_list_volumes;
246   iface->list_roots = gtk_file_system_unix_list_roots;
247   iface->get_folder = gtk_file_system_unix_get_folder;
248   iface->get_root_info = gtk_file_system_unix_get_root_info;
249   iface->create_folder = gtk_file_system_unix_create_folder;
250   iface->volume_free = gtk_file_system_unix_volume_free;
251   iface->volume_get_base_path = gtk_file_system_unix_volume_get_base_path;
252   iface->volume_get_is_mounted = gtk_file_system_unix_volume_get_is_mounted;
253   iface->volume_mount = gtk_file_system_unix_volume_mount;
254   iface->volume_get_display_name = gtk_file_system_unix_volume_get_display_name;
255   iface->volume_render_icon = gtk_file_system_unix_volume_render_icon;
256   iface->get_parent = gtk_file_system_unix_get_parent;
257   iface->make_path = gtk_file_system_unix_make_path;
258   iface->parse = gtk_file_system_unix_parse;
259   iface->path_to_uri = gtk_file_system_unix_path_to_uri;
260   iface->path_to_filename = gtk_file_system_unix_path_to_filename;
261   iface->uri_to_path = gtk_file_system_unix_uri_to_path;
262   iface->filename_to_path = gtk_file_system_unix_filename_to_path;
263   iface->render_icon = gtk_file_system_unix_render_icon;
264   iface->add_bookmark = gtk_file_system_unix_add_bookmark;
265   iface->remove_bookmark = gtk_file_system_unix_remove_bookmark;
266   iface->list_bookmarks = gtk_file_system_unix_list_bookmarks;
267 }
268
269 static void
270 gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
271 {
272 }
273
274 static void
275 gtk_file_system_unix_finalize (GObject *object)
276 {
277   system_parent_class->finalize (object);
278 }
279
280 static GSList *
281 gtk_file_system_unix_list_volumes (GtkFileSystem *file_system)
282 {
283   return g_slist_append (NULL, gtk_file_path_new_dup ("/"));
284 }
285
286 static GSList *
287 gtk_file_system_unix_list_roots (GtkFileSystem *file_system)
288 {
289   return g_slist_append (NULL, gtk_file_path_new_dup ("/"));
290 }
291
292 static GtkFileInfo *
293 gtk_file_system_unix_get_root_info (GtkFileSystem    *file_system,
294                                     const GtkFilePath      *path,
295                                     GtkFileInfoType   types,
296                                     GError          **error)
297 {
298   const gchar *filename = gtk_file_path_get_string (path);
299
300   g_return_val_if_fail (strcmp (filename, "/") == 0, NULL);
301
302   return filename_get_info ("/", types, error);
303 }
304
305 static GtkFileFolder *
306 gtk_file_system_unix_get_folder (GtkFileSystem     *file_system,
307                                  const GtkFilePath *path,
308                                  GtkFileInfoType    types,
309                                  GError           **error)
310 {
311   GtkFileFolderUnix *folder_unix;
312   gchar *filename;
313
314   filename = filename_from_path (path);
315   g_return_val_if_fail (filename != NULL, NULL);
316
317   folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
318   folder_unix->filename = filename;
319   folder_unix->types = types;
320
321   return GTK_FILE_FOLDER (folder_unix);
322 }
323
324 static gboolean
325 gtk_file_system_unix_create_folder (GtkFileSystem     *file_system,
326                                     const GtkFilePath *path,
327                                     GError           **error)
328 {
329   gchar *filename;
330   gboolean result;
331
332   filename = filename_from_path (path);
333   g_return_val_if_fail (filename != NULL, FALSE);
334
335   result = mkdir (filename, 0777) != 0;
336
337   if (!result)
338     {
339       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
340       g_set_error (error,
341                    GTK_FILE_SYSTEM_ERROR,
342                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
343                    _("error creating directory '%s': %s"),
344                    filename_utf8 ? filename_utf8 : "???",
345                    g_strerror (errno));
346       g_free (filename_utf8);
347     }
348
349   g_free (filename);
350
351   return result;
352 }
353
354 static void
355 gtk_file_system_unix_volume_free (GtkFileSystem        *file_system,
356                                   GtkFileSystemVolume  *volume)
357 {
358   GtkFilePath *path;
359
360   path = (GtkFilePath *) volume;
361   gtk_file_path_free (path);
362 }
363
364 static GtkFilePath *
365 gtk_file_system_unix_volume_get_base_path (GtkFileSystem        *file_system,
366                                            GtkFileSystemVolume  *volume)
367 {
368   return gtk_file_path_new_dup ("/");
369 }
370
371 static gboolean
372 gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem        *file_system,
373                                             GtkFileSystemVolume  *volume)
374 {
375   return TRUE;
376 }
377
378 static gboolean
379 gtk_file_system_unix_volume_mount (GtkFileSystem        *file_system,
380                                    GtkFileSystemVolume  *volume,
381                                    GError              **error)
382 {
383   g_set_error (error,
384                GTK_FILE_SYSTEM_ERROR,
385                GTK_FILE_SYSTEM_ERROR_FAILED,
386                _("This file system does not support mounting"));
387   return FALSE;
388 }
389
390 static gchar *
391 gtk_file_system_unix_volume_get_display_name (GtkFileSystem       *file_system,
392                                               GtkFileSystemVolume *volume)
393 {
394   return g_strdup (_("Filesystem")); /* Same as Nautilus */
395 }
396
397 static GdkPixbuf *
398 gtk_file_system_unix_volume_render_icon (GtkFileSystem        *file_system,
399                                          GtkFileSystemVolume  *volume,
400                                          GtkWidget            *widget,
401                                          gint                  pixel_size,
402                                          GError              **error)
403 {
404   return NULL; /* FIXME: We need a hard disk icon or something */
405 }
406
407 static gboolean
408 gtk_file_system_unix_get_parent (GtkFileSystem     *file_system,
409                                  const GtkFilePath *path,
410                                  GtkFilePath      **parent,
411                                  GError           **error)
412 {
413   gchar *filename = filename_from_path (path);
414   g_return_val_if_fail (filename != NULL, FALSE);
415
416   if (filename_is_root (filename))
417     {
418       *parent = NULL;
419     }
420   else
421     {
422       gchar *parent_filename = g_path_get_dirname (filename);
423       *parent = filename_to_path (parent_filename);
424       g_free (parent_filename);
425     }
426
427   g_free (filename);
428
429   return TRUE;
430 }
431
432 static GtkFilePath *
433 gtk_file_system_unix_make_path (GtkFileSystem    *file_system,
434                                const GtkFilePath *base_path,
435                                const gchar       *display_name,
436                                GError           **error)
437 {
438   gchar *base_filename;
439   gchar *filename;
440   gchar *full_filename;
441   GError *tmp_error = NULL;
442   GtkFilePath *result;
443
444   base_filename = filename_from_path (base_path);
445   g_return_val_if_fail (base_filename != NULL, NULL);
446
447   filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
448   if (!filename)
449     {
450       g_set_error (error,
451                    GTK_FILE_SYSTEM_ERROR,
452                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
453                    "%s",
454                    tmp_error->message);
455
456       g_error_free (tmp_error);
457       g_free (base_filename);
458
459       return FALSE;
460     }
461
462   full_filename = g_build_filename (base_filename, filename, NULL);
463   result = filename_to_path (full_filename);
464   g_free (base_filename);
465   g_free (filename);
466   g_free (full_filename);
467
468   return result;
469 }
470
471 /* If this was a publically exported function, it should return
472  * a dup'ed result, but we make it modify-in-place for efficiency
473  * here, and because it works for us.
474  */
475 static void
476 canonicalize_filename (gchar *filename)
477 {
478   gchar *p, *q;
479   gboolean last_was_slash = FALSE;
480
481   p = filename;
482   q = filename;
483
484   while (*p)
485     {
486       if (*p == G_DIR_SEPARATOR)
487         {
488           if (!last_was_slash)
489             *q++ = G_DIR_SEPARATOR;
490
491           last_was_slash = TRUE;
492         }
493       else
494         {
495           if (last_was_slash && *p == '.')
496             {
497               if (*(p + 1) == G_DIR_SEPARATOR ||
498                   *(p + 1) == '\0')
499                 {
500                   if (*(p + 1) == '\0')
501                     break;
502
503                   p += 1;
504                 }
505               else if (*(p + 1) == '.' &&
506                        (*(p + 2) == G_DIR_SEPARATOR ||
507                         *(p + 2) == '\0'))
508                 {
509                   if (q > filename + 1)
510                     {
511                       q--;
512                       while (q > filename + 1 &&
513                              *(q - 1) != G_DIR_SEPARATOR)
514                         q--;
515                     }
516
517                   if (*(p + 2) == '\0')
518                     break;
519
520                   p += 2;
521                 }
522               else
523                 {
524                   *q++ = *p;
525                   last_was_slash = FALSE;
526                 }
527             }
528           else
529             {
530               *q++ = *p;
531               last_was_slash = FALSE;
532             }
533         }
534
535       p++;
536     }
537
538   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
539     q--;
540
541   *q = '\0';
542 }
543
544 static gboolean
545 gtk_file_system_unix_parse (GtkFileSystem     *file_system,
546                             const GtkFilePath *base_path,
547                             const gchar       *str,
548                             GtkFilePath      **folder,
549                             gchar            **file_part,
550                             GError           **error)
551 {
552   char *base_filename;
553   gchar *last_slash;
554   gboolean result = FALSE;
555
556   base_filename = filename_from_path (base_path);
557   g_return_val_if_fail (base_filename != NULL, FALSE);
558
559   last_slash = strrchr (str, G_DIR_SEPARATOR);
560   if (!last_slash)
561     {
562       *folder = gtk_file_path_copy (base_path);
563       *file_part = g_strdup (str);
564       result = TRUE;
565     }
566   else
567     {
568       gchar *folder_part;
569       gchar *folder_path;
570       GError *tmp_error = NULL;
571
572       if (last_slash == str)
573         folder_part = g_strdup ("/");
574       else
575         folder_part = g_filename_from_utf8 (str, last_slash - str,
576                                             NULL, NULL, &tmp_error);
577
578       if (!folder_part)
579         {
580           g_set_error (error,
581                        GTK_FILE_SYSTEM_ERROR,
582                        GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
583                        "%s",
584                        tmp_error->message);
585           g_error_free (tmp_error);
586         }
587       else
588         {
589           if (folder_part[0] == G_DIR_SEPARATOR)
590             folder_path = folder_part;
591           else
592             {
593               folder_path = g_build_filename (base_filename, folder_part, NULL);
594               g_free (folder_part);
595             }
596
597           canonicalize_filename (folder_path);
598
599           *folder = filename_to_path (folder_path);
600           *file_part = g_strdup (last_slash + 1);
601
602           g_free (folder_path);
603
604           result = TRUE;
605         }
606     }
607
608   g_free (base_filename);
609
610   return result;
611 }
612
613 static gchar *
614 gtk_file_system_unix_path_to_uri (GtkFileSystem     *file_system,
615                                   const GtkFilePath *path)
616 {
617   return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
618 }
619
620 static gchar *
621 gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
622                                        const GtkFilePath *path)
623 {
624   return g_strdup (gtk_file_path_get_string (path));
625 }
626
627 static GtkFilePath *
628 gtk_file_system_unix_uri_to_path (GtkFileSystem     *file_system,
629                                   const gchar       *uri)
630 {
631   gchar *filename = g_filename_from_uri (uri, NULL, NULL);
632   if (filename)
633     return gtk_file_path_new_steal (filename);
634   else
635     return NULL;
636 }
637
638 static GtkFilePath *
639 gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
640                                        const gchar   *filename)
641 {
642   return gtk_file_path_new_dup (filename);
643 }
644
645 static GdkPixbuf *
646 gtk_file_system_unix_render_icon (GtkFileSystem     *file_system,
647                                   const GtkFilePath *path,
648                                   GtkWidget         *widget,
649                                   gint               pixel_size,
650                                   GError           **error)
651 {
652   /* FIXME: Implement this */
653   g_set_error (error,
654                GTK_FILE_SYSTEM_ERROR,
655                GTK_FILE_SYSTEM_ERROR_FAILED,
656                _("This file system does not support icons"));
657   return NULL;
658 }
659
660 static gboolean
661 gtk_file_system_unix_add_bookmark (GtkFileSystem     *file_system,
662                                    const GtkFilePath *path,
663                                    GError           **error)
664 {
665   /* FIXME: Implement as a really simple ~/.gtk-bookmarks */
666   g_set_error (error,
667                GTK_FILE_SYSTEM_ERROR,
668                GTK_FILE_SYSTEM_ERROR_FAILED,
669                _("This file system does not support bookmarks"));
670   return FALSE;
671 }
672
673 static gboolean
674 gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
675                                       const GtkFilePath *path,
676                                       GError           **error)
677 {
678   /* FIXME: Implement as a really simple ~/.gtk-bookmarks */
679   g_set_error (error,
680                GTK_FILE_SYSTEM_ERROR,
681                GTK_FILE_SYSTEM_ERROR_FAILED,
682                _("This file system does not support bookmarks"));
683   return FALSE;
684 }
685
686 static GSList *
687 gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
688 {
689   /* FIXME: Implement as a really simple ~/.gtk-bookmarks */
690   return NULL;
691 }
692
693 /*
694  * GtkFileFolderUnix
695  */
696 static GType
697 gtk_file_folder_unix_get_type (void)
698 {
699   static GType file_folder_unix_type = 0;
700
701   if (!file_folder_unix_type)
702     {
703       static const GTypeInfo file_folder_unix_info =
704       {
705         sizeof (GtkFileFolderUnixClass),
706         NULL,           /* base_init */
707         NULL,           /* base_finalize */
708         (GClassInitFunc) gtk_file_folder_unix_class_init,
709         NULL,           /* class_finalize */
710         NULL,           /* class_data */
711         sizeof (GtkFileFolderUnix),
712         0,              /* n_preallocs */
713         (GInstanceInitFunc) gtk_file_folder_unix_init,
714       };
715
716       static const GInterfaceInfo file_folder_info =
717       {
718         (GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
719         NULL,                                                 /* interface_finalize */
720         NULL                                                  /* interface_data */
721       };
722
723       file_folder_unix_type = g_type_register_static (G_TYPE_OBJECT,
724                                                       "GtkFileFolderUnix",
725                                                       &file_folder_unix_info, 0);
726       g_type_add_interface_static (file_folder_unix_type,
727                                    GTK_TYPE_FILE_FOLDER,
728                                    &file_folder_info);
729     }
730
731   return file_folder_unix_type;
732 }
733
734 static void
735 gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
736 {
737   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
738
739   folder_parent_class = g_type_class_peek_parent (class);
740
741   gobject_class->finalize = gtk_file_folder_unix_finalize;
742 }
743
744 static void
745 gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
746 {
747   iface->get_info = gtk_file_folder_unix_get_info;
748   iface->list_children = gtk_file_folder_unix_list_children;
749 }
750
751 static void
752 gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
753 {
754 }
755
756 static void
757 gtk_file_folder_unix_finalize (GObject *object)
758 {
759   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
760
761   g_free (folder_unix->filename);
762
763   folder_parent_class->finalize (object);
764 }
765
766 static GtkFileInfo *
767 gtk_file_folder_unix_get_info (GtkFileFolder  *folder,
768                                const GtkFilePath    *path,
769                                GError        **error)
770 {
771   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
772   GtkFileInfo *info;
773   gchar *dirname;
774   gchar *filename;
775
776   filename = filename_from_path (path);
777   g_return_val_if_fail (filename != NULL, NULL);
778
779   dirname = g_path_get_dirname (filename);
780   g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
781   g_free (dirname);
782
783   info = filename_get_info (filename, folder_unix->types, error);
784
785   g_free (filename);
786
787   return info;
788 }
789
790 static gboolean
791 gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
792                                     GSList        **children,
793                                     GError        **error)
794 {
795   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
796   GError *tmp_error = NULL;
797   GDir *dir;
798
799   *children = NULL;
800
801   dir = g_dir_open (folder_unix->filename, 0, &tmp_error);
802   if (!dir)
803     {
804       g_set_error (error,
805                    GTK_FILE_SYSTEM_ERROR,
806                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
807                    "%s",
808                    tmp_error->message);
809
810       g_error_free (tmp_error);
811
812       return FALSE;
813     }
814
815   while (TRUE)
816     {
817       const gchar *filename = g_dir_read_name (dir);
818       gchar *fullname;
819
820       if (!filename)
821         break;
822
823       fullname = g_build_filename (folder_unix->filename, filename, NULL);
824       *children = g_slist_prepend (*children, filename_to_path (fullname));
825       g_free (fullname);
826     }
827
828   g_dir_close (dir);
829
830   *children = g_slist_reverse (*children);
831
832   return TRUE;
833 }
834
835 static GtkFileInfo *
836 filename_get_info (const gchar     *filename,
837                    GtkFileInfoType  types,
838                    GError         **error)
839 {
840   GtkFileInfo *info;
841 #if 0
842   GtkFileIconType icon_type = GTK_FILE_ICON_REGULAR;
843 #endif
844   struct stat statbuf;
845
846   /* If stat fails, try to fall back to lstat to catch broken links
847    */
848   if (stat (filename, &statbuf) != 0 &&
849       lstat (filename, &statbuf) != 0)
850     {
851       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
852       g_set_error (error,
853                    GTK_FILE_SYSTEM_ERROR,
854                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
855                    _("error getting information for '%s': %s"),
856                    filename_utf8 ? filename_utf8 : "???",
857                    g_strerror (errno));
858       g_free (filename_utf8);
859
860       return NULL;
861     }
862
863   info = gtk_file_info_new ();
864
865   if (filename_is_root (filename))
866     {
867       if (types & GTK_FILE_INFO_DISPLAY_NAME)
868         gtk_file_info_set_display_name (info, "/");
869
870       if (types & GTK_FILE_INFO_IS_HIDDEN)
871         gtk_file_info_set_is_hidden (info, FALSE);
872     }
873   else
874     {
875       gchar *basename = g_path_get_basename (filename);
876
877       if (types & GTK_FILE_INFO_DISPLAY_NAME)
878         {
879           gchar *display_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
880           if (!display_name)
881             display_name = g_strescape (basename, NULL);
882
883           gtk_file_info_set_display_name (info, display_name);
884
885           g_free (display_name);
886         }
887
888       if (types & GTK_FILE_INFO_IS_HIDDEN)
889         {
890           gtk_file_info_set_is_hidden (info, basename[0] == '.');
891         }
892
893       g_free (basename);
894     }
895
896   if (types & GTK_FILE_INFO_IS_FOLDER)
897     {
898       gtk_file_info_set_is_folder (info, S_ISDIR (statbuf.st_mode));
899    }
900
901 #if 0
902   if (types & GTK_FILE_INFO_ICON)
903     {
904       if (S_ISBLK (statbuf.st_mode))
905         icon_type = GTK_FILE_ICON_BLOCK_DEVICE;
906       else if (S_ISLNK (statbuf.st_mode))
907         icon_type = GTK_FILE_ICON_BROKEN_SYMBOLIC_LINK;
908       else if (S_ISCHR (statbuf.st_mode))
909         icon_type = GTK_FILE_ICON_CHARACTER_DEVICE;
910       else if (S_ISDIR (statbuf.st_mode))
911         icon_type = GTK_FILE_ICON_DIRECTORY;
912       else if (S_ISFIFO (statbuf.st_mode))
913         icon_type =  GTK_FILE_ICON_FIFO;
914       else if (S_ISSOCK (statbuf.st_mode))
915         icon_type = GTK_FILE_ICON_SOCKET;
916
917       gtk_file_info_set_icon_type (info, icon_type);
918     }
919
920   if ((types & GTK_FILE_INFO_MIME_TYPE) ||
921       ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR))
922 #else
923   if (types & GTK_FILE_INFO_MIME_TYPE)
924 #endif
925     {
926       const char *mime_type = xdg_mime_get_mime_type_for_file (filename);
927       gtk_file_info_set_mime_type (info, mime_type);
928
929 #if 0
930       if ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR &&
931           (statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) &&
932           (strcmp (mime_type, XDG_MIME_TYPE_UNKNOWN) == 0 ||
933            strcmp (mime_type, "application/x-executable") == 0 ||
934            strcmp (mime_type, "application/x-shellscript") == 0))
935         gtk_file_info_set_icon_type (info, GTK_FILE_ICON_EXECUTABLE);
936 #endif
937     }
938
939   if (types & GTK_FILE_INFO_MODIFICATION_TIME)
940     {
941       gtk_file_info_set_modification_time (info, statbuf.st_mtime);
942     }
943
944   if (types & GTK_FILE_INFO_SIZE)
945     {
946       gtk_file_info_set_size (info, (gint64)statbuf.st_size);
947     }
948
949   return info;
950 }
951
952 static gchar *
953 filename_from_path (const GtkFilePath *path)
954 {
955   return g_strdup (gtk_file_path_get_string (path));
956 }
957
958 static GtkFilePath *
959 filename_to_path (const char *filename)
960 {
961   return gtk_file_path_new_dup (filename);
962 }
963
964 static gboolean
965 filename_is_root (const char *filename)
966 {
967   const gchar *after_root;
968
969   after_root = g_path_skip_root (filename);
970
971   return (after_root != NULL && *after_root == '\0');
972 }