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