]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemunix.c
Use g_file_set_contents(). (#308722, 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 #include "gtkstock.h"
28 #include "gtkalias.h"
29
30 #define XDG_PREFIX _gtk_xdg
31 #include "xdgmime/xdgmime.h"
32
33 #include <errno.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <pwd.h>
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #include <stdio.h>
42 #include <time.h>
43
44 #define BOOKMARKS_FILENAME ".gtk-bookmarks"
45
46 #define HIDDEN_FILENAME ".hidden"
47
48 #define FOLDER_CACHE_LIFETIME 2 /* seconds */
49
50 typedef struct _GtkFileSystemUnixClass GtkFileSystemUnixClass;
51
52 #define GTK_FILE_SYSTEM_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
53 #define GTK_IS_FILE_SYSTEM_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_UNIX))
54 #define GTK_FILE_SYSTEM_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
55
56 struct _GtkFileSystemUnixClass
57 {
58   GObjectClass parent_class;
59 };
60
61 struct _GtkFileSystemUnix
62 {
63   GObject parent_instance;
64
65   GHashTable *folder_hash;
66
67   /* For /afs and /net */
68   struct stat afs_statbuf;
69   struct stat net_statbuf;
70
71   guint have_afs : 1;
72   guint have_net : 1;
73 };
74
75 /* Icon type, supplemented by MIME type
76  */
77 typedef enum {
78   ICON_UNDECIDED, /* Only used while we have not yet computed the icon in a struct stat_info_entry */
79   ICON_NONE,      /* "Could not compute the icon type" */
80   ICON_REGULAR,   /* Use mime type for icon */
81   ICON_BLOCK_DEVICE,
82   ICON_BROKEN_SYMBOLIC_LINK,
83   ICON_CHARACTER_DEVICE,
84   ICON_DIRECTORY,
85   ICON_EXECUTABLE,
86   ICON_FIFO,
87   ICON_SOCKET
88 } IconType;
89
90
91 #define GTK_TYPE_FILE_FOLDER_UNIX             (gtk_file_folder_unix_get_type ())
92 #define GTK_FILE_FOLDER_UNIX(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnix))
93 #define GTK_IS_FILE_FOLDER_UNIX(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_UNIX))
94 #define GTK_FILE_FOLDER_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
95 #define GTK_IS_FILE_FOLDER_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_UNIX))
96 #define GTK_FILE_FOLDER_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
97
98 typedef struct _GtkFileFolderUnix      GtkFileFolderUnix;
99 typedef struct _GtkFileFolderUnixClass GtkFileFolderUnixClass;
100
101 struct _GtkFileFolderUnixClass
102 {
103   GObjectClass parent_class;
104 };
105
106 struct _GtkFileFolderUnix
107 {
108   GObject parent_instance;
109
110   GtkFileSystemUnix *system_unix;
111   GtkFileInfoType types;
112   gchar *filename;
113   GHashTable *stat_info;
114   guint have_stat : 1;
115   guint have_mime_type : 1;
116   guint is_network_dir : 1;
117   guint have_hidden : 1;
118   time_t asof;
119 };
120
121 struct stat_info_entry {
122   struct stat statbuf;
123   char *mime_type;
124   IconType icon_type;
125   gboolean hidden;
126 };
127
128 static const GtkFileInfoType STAT_NEEDED_MASK = (GTK_FILE_INFO_IS_FOLDER |
129                                                  GTK_FILE_INFO_MODIFICATION_TIME |
130                                                  GTK_FILE_INFO_SIZE);
131
132 static GObjectClass *system_parent_class;
133 static GObjectClass *folder_parent_class;
134
135 static void gtk_file_system_unix_class_init   (GtkFileSystemUnixClass *class);
136 static void gtk_file_system_unix_iface_init   (GtkFileSystemIface     *iface);
137 static void gtk_file_system_unix_init         (GtkFileSystemUnix      *impl);
138 static void gtk_file_system_unix_finalize     (GObject                *object);
139
140 static GSList *             gtk_file_system_unix_list_volumes        (GtkFileSystem     *file_system);
141 static GtkFileSystemVolume *gtk_file_system_unix_get_volume_for_path (GtkFileSystem     *file_system,
142                                                                       const GtkFilePath *path);
143
144 static GtkFileFolder *gtk_file_system_unix_get_folder    (GtkFileSystem      *file_system,
145                                                           const GtkFilePath  *path,
146                                                           GtkFileInfoType     types,
147                                                           GError            **error);
148 static gboolean       gtk_file_system_unix_create_folder (GtkFileSystem      *file_system,
149                                                           const GtkFilePath  *path,
150                                                           GError            **error);
151
152 static void         gtk_file_system_unix_volume_free             (GtkFileSystem       *file_system,
153                                                                   GtkFileSystemVolume *volume);
154 static GtkFilePath *gtk_file_system_unix_volume_get_base_path    (GtkFileSystem       *file_system,
155                                                                   GtkFileSystemVolume *volume);
156 static gboolean     gtk_file_system_unix_volume_get_is_mounted   (GtkFileSystem       *file_system,
157                                                                   GtkFileSystemVolume *volume);
158 static gboolean     gtk_file_system_unix_volume_mount            (GtkFileSystem       *file_system,
159                                                                   GtkFileSystemVolume *volume,
160                                                                   GError             **error);
161 static gchar *      gtk_file_system_unix_volume_get_display_name (GtkFileSystem       *file_system,
162                                                                   GtkFileSystemVolume *volume);
163 static GdkPixbuf *  gtk_file_system_unix_volume_render_icon      (GtkFileSystem        *file_system,
164                                                                   GtkFileSystemVolume  *volume,
165                                                                   GtkWidget            *widget,
166                                                                   gint                  pixel_size,
167                                                                   GError              **error);
168
169 static gboolean       gtk_file_system_unix_get_parent    (GtkFileSystem      *file_system,
170                                                           const GtkFilePath  *path,
171                                                           GtkFilePath       **parent,
172                                                           GError            **error);
173 static GtkFilePath *  gtk_file_system_unix_make_path     (GtkFileSystem      *file_system,
174                                                           const GtkFilePath  *base_path,
175                                                           const gchar        *display_name,
176                                                           GError            **error);
177 static gboolean       gtk_file_system_unix_parse         (GtkFileSystem      *file_system,
178                                                           const GtkFilePath  *base_path,
179                                                           const gchar        *str,
180                                                           GtkFilePath       **folder,
181                                                           gchar             **file_part,
182                                                           GError            **error);
183
184 static gchar *      gtk_file_system_unix_path_to_uri      (GtkFileSystem     *file_system,
185                                                            const GtkFilePath *path);
186 static gchar *      gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
187                                                            const GtkFilePath *path);
188 static GtkFilePath *gtk_file_system_unix_uri_to_path      (GtkFileSystem     *file_system,
189                                                            const gchar       *uri);
190 static GtkFilePath *gtk_file_system_unix_filename_to_path (GtkFileSystem     *file_system,
191                                                            const gchar       *filename);
192
193 static GdkPixbuf *gtk_file_system_unix_render_icon (GtkFileSystem     *file_system,
194                                                     const GtkFilePath *path,
195                                                     GtkWidget         *widget,
196                                                     gint               pixel_size,
197                                                     GError           **error);
198
199 static gboolean gtk_file_system_unix_insert_bookmark (GtkFileSystem     *file_system,
200                                                       const GtkFilePath *path,
201                                                       gint               position,
202                                                       GError           **error);
203 static gboolean gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
204                                                       const GtkFilePath *path,
205                                                       GError           **error);
206 static GSList * gtk_file_system_unix_list_bookmarks  (GtkFileSystem     *file_system);
207 static gchar  * gtk_file_system_unix_get_bookmark_label (GtkFileSystem     *file_system,
208                                                          const GtkFilePath *path);
209 static void     gtk_file_system_unix_set_bookmark_label (GtkFileSystem     *file_system,
210                                                          const GtkFilePath *path,
211                                                          const gchar       *label);
212
213 static GType gtk_file_folder_unix_get_type   (void);
214 static void  gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class);
215 static void  gtk_file_folder_unix_iface_init (GtkFileFolderIface     *iface);
216 static void  gtk_file_folder_unix_init       (GtkFileFolderUnix      *impl);
217 static void  gtk_file_folder_unix_finalize   (GObject                *object);
218
219 static GtkFileInfo *gtk_file_folder_unix_get_info      (GtkFileFolder  *folder,
220                                                         const GtkFilePath    *path,
221                                                         GError        **error);
222 static gboolean     gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
223                                                         GSList        **children,
224                                                         GError        **error);
225
226 static gboolean     gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder);
227
228 static GtkFilePath *filename_to_path   (const gchar       *filename);
229
230 static gboolean     filename_is_root  (const char       *filename);
231
232 static gboolean file_is_hidden (GtkFileFolderUnix *folder_unix,
233                                 const char        *basename);
234
235 static gboolean fill_in_names (GtkFileFolderUnix *folder_unix, GError **error);
236 static void fill_in_stats (GtkFileFolderUnix *folder_unix);
237 static void fill_in_mime_type (GtkFileFolderUnix *folder_unix);
238 static void fill_in_hidden (GtkFileFolderUnix *folder_unix);
239
240 static char *       get_parent_dir    (const char       *filename);
241
242 /*
243  * GtkFileSystemUnix
244  */
245 GType
246 gtk_file_system_unix_get_type (void)
247 {
248   static GType file_system_unix_type = 0;
249
250   if (!file_system_unix_type)
251     {
252       static const GTypeInfo file_system_unix_info =
253       {
254         sizeof (GtkFileSystemUnixClass),
255         NULL,           /* base_init */
256         NULL,           /* base_finalize */
257         (GClassInitFunc) gtk_file_system_unix_class_init,
258         NULL,           /* class_finalize */
259         NULL,           /* class_data */
260         sizeof (GtkFileSystemUnix),
261         0,              /* n_preallocs */
262         (GInstanceInitFunc) gtk_file_system_unix_init,
263       };
264
265       static const GInterfaceInfo file_system_info =
266       {
267         (GInterfaceInitFunc) gtk_file_system_unix_iface_init, /* interface_init */
268         NULL,                                                 /* interface_finalize */
269         NULL                                                  /* interface_data */
270       };
271
272       file_system_unix_type = g_type_register_static (G_TYPE_OBJECT,
273                                                       "GtkFileSystemUnix",
274                                                       &file_system_unix_info, 0);
275       g_type_add_interface_static (file_system_unix_type,
276                                    GTK_TYPE_FILE_SYSTEM,
277                                    &file_system_info);
278     }
279
280   return file_system_unix_type;
281 }
282
283 /**
284  * gtk_file_system_unix_new:
285  *
286  * Creates a new #GtkFileSystemUnix object. #GtkFileSystemUnix
287  * implements the #GtkFileSystem interface with direct access to
288  * the filesystem using Unix/Linux API calls
289  *
290  * Return value: the new #GtkFileSystemUnix object
291  **/
292 GtkFileSystem *
293 gtk_file_system_unix_new (void)
294 {
295   return g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL);
296 }
297
298 static void
299 gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class)
300 {
301   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
302
303   system_parent_class = g_type_class_peek_parent (class);
304
305   gobject_class->finalize = gtk_file_system_unix_finalize;
306 }
307
308 static void
309 gtk_file_system_unix_iface_init   (GtkFileSystemIface *iface)
310 {
311   iface->list_volumes = gtk_file_system_unix_list_volumes;
312   iface->get_volume_for_path = gtk_file_system_unix_get_volume_for_path;
313   iface->get_folder = gtk_file_system_unix_get_folder;
314   iface->create_folder = gtk_file_system_unix_create_folder;
315   iface->volume_free = gtk_file_system_unix_volume_free;
316   iface->volume_get_base_path = gtk_file_system_unix_volume_get_base_path;
317   iface->volume_get_is_mounted = gtk_file_system_unix_volume_get_is_mounted;
318   iface->volume_mount = gtk_file_system_unix_volume_mount;
319   iface->volume_get_display_name = gtk_file_system_unix_volume_get_display_name;
320   iface->volume_render_icon = gtk_file_system_unix_volume_render_icon;
321   iface->get_parent = gtk_file_system_unix_get_parent;
322   iface->make_path = gtk_file_system_unix_make_path;
323   iface->parse = gtk_file_system_unix_parse;
324   iface->path_to_uri = gtk_file_system_unix_path_to_uri;
325   iface->path_to_filename = gtk_file_system_unix_path_to_filename;
326   iface->uri_to_path = gtk_file_system_unix_uri_to_path;
327   iface->filename_to_path = gtk_file_system_unix_filename_to_path;
328   iface->render_icon = gtk_file_system_unix_render_icon;
329   iface->insert_bookmark = gtk_file_system_unix_insert_bookmark;
330   iface->remove_bookmark = gtk_file_system_unix_remove_bookmark;
331   iface->list_bookmarks = gtk_file_system_unix_list_bookmarks;
332   iface->get_bookmark_label = gtk_file_system_unix_get_bookmark_label;
333   iface->set_bookmark_label = gtk_file_system_unix_set_bookmark_label;
334 }
335
336 static void
337 gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
338 {
339   system_unix->folder_hash = g_hash_table_new (g_str_hash, g_str_equal);
340
341   if (stat ("/afs", &system_unix->afs_statbuf) == 0)
342     system_unix->have_afs = TRUE;
343   else
344     system_unix->have_afs = FALSE;
345
346   if (stat ("/net", &system_unix->net_statbuf) == 0)
347     system_unix->have_net = TRUE;
348   else
349     system_unix->have_net = FALSE;
350 }
351
352 static void
353 gtk_file_system_unix_finalize (GObject *object)
354 {
355   GtkFileSystemUnix *system_unix;
356
357   system_unix = GTK_FILE_SYSTEM_UNIX (object);
358
359   /* FIXME: assert that the hash is empty? */
360   g_hash_table_destroy (system_unix->folder_hash);
361
362   system_parent_class->finalize (object);
363 }
364
365 /* Returns our single root volume */
366 static GtkFileSystemVolume *
367 get_root_volume (void)
368 {
369   return (GtkFileSystemVolume *) gtk_file_path_new_dup ("/");
370 }
371
372 static GSList *
373 gtk_file_system_unix_list_volumes (GtkFileSystem *file_system)
374 {
375   return g_slist_append (NULL, get_root_volume ());
376 }
377
378 static GtkFileSystemVolume *
379 gtk_file_system_unix_get_volume_for_path (GtkFileSystem     *file_system,
380                                           const GtkFilePath *path)
381 {
382   return get_root_volume ();
383 }
384
385 static char *
386 remove_trailing_slash (const char *filename)
387 {
388   int len;
389
390   len = strlen (filename);
391
392   if (len > 1 && filename[len - 1] == '/')
393     return g_strndup (filename, len - 1);
394   else
395     return g_memdup (filename, len + 1);
396 }
397
398 static GtkFileFolder *
399 gtk_file_system_unix_get_folder (GtkFileSystem     *file_system,
400                                  const GtkFilePath *path,
401                                  GtkFileInfoType    types,
402                                  GError           **error)
403 {
404   GtkFileSystemUnix *system_unix;
405   GtkFileFolderUnix *folder_unix;
406   const char *filename;
407   char *filename_copy;
408   time_t now = time (NULL);
409
410   system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
411
412   filename = gtk_file_path_get_string (path);
413   g_return_val_if_fail (filename != NULL, NULL);
414   g_return_val_if_fail (g_path_is_absolute (filename), NULL);
415
416   filename_copy = remove_trailing_slash (filename);
417   folder_unix = g_hash_table_lookup (system_unix->folder_hash, filename_copy);
418
419   if (folder_unix)
420     {
421       g_free (filename_copy);
422       if (now - folder_unix->asof >= FOLDER_CACHE_LIFETIME &&
423           folder_unix->stat_info)
424         {
425 #if 0
426           g_print ("Cleaning out cached directory %s\n", filename);
427 #endif
428           g_hash_table_destroy (folder_unix->stat_info);
429           folder_unix->stat_info = NULL;
430           folder_unix->have_mime_type = FALSE;
431           folder_unix->have_stat = FALSE;
432           folder_unix->have_hidden = FALSE;
433         }
434
435       g_object_ref (folder_unix);
436       folder_unix->types |= types;
437       types = folder_unix->types;
438     }
439   else
440     {
441       struct stat statbuf;
442       int result;
443       int code;
444       int my_errno;
445
446       code = my_errno = 0; /* shut up GCC */
447
448       result = stat (filename, &statbuf);
449
450       if (result == 0)
451         {
452           if (!S_ISDIR (statbuf.st_mode))
453             {
454               result = -1;
455               code = GTK_FILE_SYSTEM_ERROR_NOT_FOLDER;
456               my_errno = ENOTDIR;
457             }
458         }
459       else
460         {
461           my_errno = errno;
462
463           if (my_errno == ENOENT)
464             code = GTK_FILE_SYSTEM_ERROR_NONEXISTENT;
465           else
466             code = GTK_FILE_SYSTEM_ERROR_FAILED;
467         }
468
469       if (result != 0)
470         {
471           gchar *display_name = g_filename_display_name (filename);
472           g_set_error (error,
473                        GTK_FILE_SYSTEM_ERROR,
474                        code,
475                        _("Error getting information for '%s': %s"),
476                        display_name,
477                        g_strerror (my_errno));
478
479           g_free (display_name);
480           g_free (filename_copy);
481           return NULL;
482         }
483
484       folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
485       folder_unix->system_unix = system_unix;
486       folder_unix->filename = filename_copy;
487       folder_unix->types = types;
488       folder_unix->stat_info = NULL;
489       folder_unix->asof = now;
490       folder_unix->have_mime_type = FALSE;
491       folder_unix->have_stat = FALSE;
492       folder_unix->have_hidden = FALSE;
493
494       if ((system_unix->have_afs &&
495            system_unix->afs_statbuf.st_dev == statbuf.st_dev &&
496            system_unix->afs_statbuf.st_ino == statbuf.st_ino) ||
497           (system_unix->have_net &&
498            system_unix->net_statbuf.st_dev == statbuf.st_dev &&
499            system_unix->net_statbuf.st_ino == statbuf.st_ino))
500         folder_unix->is_network_dir = TRUE;
501       else
502         folder_unix->is_network_dir = FALSE;
503
504       g_hash_table_insert (system_unix->folder_hash,
505                            folder_unix->filename,
506                            folder_unix);
507     }
508
509   if ((types & STAT_NEEDED_MASK) != 0)
510     fill_in_stats (folder_unix);
511
512   if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
513     fill_in_mime_type (folder_unix);
514
515   return GTK_FILE_FOLDER (folder_unix);
516 }
517
518 static gboolean
519 gtk_file_system_unix_create_folder (GtkFileSystem     *file_system,
520                                     const GtkFilePath *path,
521                                     GError           **error)
522 {
523   GtkFileSystemUnix *system_unix;
524   const char *filename;
525   gboolean result;
526   char *parent, *tmp;
527   int save_errno = errno;
528
529   system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
530
531   filename = gtk_file_path_get_string (path);
532   g_return_val_if_fail (filename != NULL, FALSE);
533   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
534
535   tmp = remove_trailing_slash (filename);
536   errno = 0;
537   result = mkdir (tmp, 0777) == 0;
538   save_errno = errno;
539   g_free (tmp);
540
541   if (!result)
542     {
543       gchar *display_name = g_filename_display_name (filename);
544       g_set_error (error,
545                    GTK_FILE_SYSTEM_ERROR,
546                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
547                    _("Error creating directory '%s': %s"),
548                    display_name,
549                    g_strerror (save_errno));
550       g_free (display_name);
551       return FALSE;
552     }
553
554   if (filename_is_root (filename))
555     return TRUE; /* hmmm, but with no notification */
556
557   parent = get_parent_dir (filename);
558   if (parent)
559     {
560       GtkFileFolderUnix *folder_unix;
561
562       folder_unix = g_hash_table_lookup (system_unix->folder_hash, parent);
563       if (folder_unix)
564         {
565           GtkFileInfoType types;
566           GtkFilePath *parent_path;
567           GSList *paths;
568           GtkFileFolder *folder;
569
570           /* This is sort of a hack.  We re-get the folder, to ensure that the
571            * newly-created directory gets read into the folder's info hash table.
572            */
573
574           types = folder_unix->types;
575
576           parent_path = gtk_file_path_new_dup (parent);
577           folder = gtk_file_system_get_folder (file_system, parent_path, types, NULL);
578           gtk_file_path_free (parent_path);
579
580           if (folder)
581             {
582               paths = g_slist_append (NULL, (GtkFilePath *) path);
583               g_signal_emit_by_name (folder, "files-added", paths);
584               g_slist_free (paths);
585               g_object_unref (folder);
586             }
587         }
588
589       g_free (parent);
590     }
591
592   return TRUE;
593 }
594
595 static void
596 gtk_file_system_unix_volume_free (GtkFileSystem        *file_system,
597                                   GtkFileSystemVolume  *volume)
598 {
599   GtkFilePath *path;
600
601   path = (GtkFilePath *) volume;
602   gtk_file_path_free (path);
603 }
604
605 static GtkFilePath *
606 gtk_file_system_unix_volume_get_base_path (GtkFileSystem        *file_system,
607                                            GtkFileSystemVolume  *volume)
608 {
609   return gtk_file_path_new_dup ("/");
610 }
611
612 static gboolean
613 gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem        *file_system,
614                                             GtkFileSystemVolume  *volume)
615 {
616   return TRUE;
617 }
618
619 static gboolean
620 gtk_file_system_unix_volume_mount (GtkFileSystem        *file_system,
621                                    GtkFileSystemVolume  *volume,
622                                    GError              **error)
623 {
624   g_set_error (error,
625                GTK_FILE_SYSTEM_ERROR,
626                GTK_FILE_SYSTEM_ERROR_FAILED,
627                _("This file system does not support mounting"));
628   return FALSE;
629 }
630
631 static gchar *
632 gtk_file_system_unix_volume_get_display_name (GtkFileSystem       *file_system,
633                                               GtkFileSystemVolume *volume)
634 {
635   return g_strdup (_("Filesystem")); /* Same as Nautilus */
636 }
637
638 static IconType
639 get_icon_type_from_stat (struct stat *statp)
640 {
641   if (S_ISBLK (statp->st_mode))
642     return ICON_BLOCK_DEVICE;
643   else if (S_ISLNK (statp->st_mode))
644     return ICON_BROKEN_SYMBOLIC_LINK; /* See get_icon_type */
645   else if (S_ISCHR (statp->st_mode))
646     return ICON_CHARACTER_DEVICE;
647   else if (S_ISDIR (statp->st_mode))
648     return ICON_DIRECTORY;
649 #ifdef S_ISFIFO
650   else if (S_ISFIFO (statp->st_mode))
651     return  ICON_FIFO;
652 #endif
653 #ifdef S_ISSOCK
654   else if (S_ISSOCK (statp->st_mode))
655     return ICON_SOCKET;
656 #endif
657   else
658     return ICON_REGULAR;
659 }
660
661 static IconType
662 get_icon_type (const char *filename,
663                GError    **error)
664 {
665   struct stat statbuf;
666
667   /* If stat fails, try to fall back to lstat to catch broken links
668    */
669   if (stat (filename, &statbuf) != 0)
670     {
671       if (errno != ENOENT || lstat (filename, &statbuf) != 0)
672         {
673           int save_errno = errno;
674           gchar *display_name = g_filename_display_name (filename);
675           g_set_error (error,
676                        GTK_FILE_SYSTEM_ERROR,
677                        GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
678                        _("Error getting information for '%s': %s"),
679                        display_name,
680                        g_strerror (save_errno));
681           g_free (display_name);
682
683           return ICON_NONE;
684         }
685     }
686
687   return get_icon_type_from_stat (&statbuf);
688 }
689
690 typedef struct
691 {
692   gint size;
693   GdkPixbuf *pixbuf;
694 } IconCacheElement;
695
696 static void
697 icon_cache_element_free (IconCacheElement *element)
698 {
699   if (element->pixbuf)
700     g_object_unref (element->pixbuf);
701   g_free (element);
702 }
703
704 static void
705 icon_theme_changed (GtkIconTheme *icon_theme)
706 {
707   GHashTable *cache;
708
709   /* Difference from the initial creation is that we don't
710    * reconnect the signal
711    */
712   cache = g_hash_table_new_full (g_str_hash, g_str_equal,
713                                  (GDestroyNotify)g_free,
714                                  (GDestroyNotify)icon_cache_element_free);
715   g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
716                           cache, (GDestroyNotify)g_hash_table_destroy);
717 }
718
719 static GdkPixbuf *
720 get_cached_icon (GtkWidget   *widget,
721                  const gchar *name,
722                  gint         pixel_size)
723 {
724   GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
725   GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
726   IconCacheElement *element;
727
728   if (!cache)
729     {
730       cache = g_hash_table_new_full (g_str_hash, g_str_equal,
731                                      (GDestroyNotify)g_free,
732                                      (GDestroyNotify)icon_cache_element_free);
733
734       g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
735                               cache, (GDestroyNotify)g_hash_table_destroy);
736       g_signal_connect (icon_theme, "changed",
737                         G_CALLBACK (icon_theme_changed), NULL);
738     }
739
740   element = g_hash_table_lookup (cache, name);
741   if (!element)
742     {
743       element = g_new0 (IconCacheElement, 1);
744       g_hash_table_insert (cache, g_strdup (name), element);
745     }
746
747   if (element->size != pixel_size)
748     {
749       if (element->pixbuf)
750         g_object_unref (element->pixbuf);
751       element->size = pixel_size;
752       element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
753                                                   pixel_size, 0, NULL);
754     }
755
756   return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
757 }
758
759 /* Renders a fallback icon from the stock system */
760 static GdkPixbuf *
761 get_fallback_icon (GtkWidget *widget,
762                    IconType   icon_type,
763                    GError   **error)
764 {
765   const char *stock_name;
766   GdkPixbuf *pixbuf;
767
768   switch (icon_type)
769     {
770     case ICON_BLOCK_DEVICE:
771       stock_name = GTK_STOCK_HARDDISK;
772       break;
773
774     case ICON_DIRECTORY:
775       stock_name = GTK_STOCK_DIRECTORY;
776       break;
777
778     case ICON_EXECUTABLE:
779       stock_name = GTK_STOCK_EXECUTE;
780       break;
781
782     default:
783       stock_name = GTK_STOCK_FILE;
784       break;
785     }
786
787   pixbuf = gtk_widget_render_icon (widget, stock_name, GTK_ICON_SIZE_SMALL_TOOLBAR, NULL);
788   if (!pixbuf)
789     g_set_error (error,
790                  GTK_FILE_SYSTEM_ERROR,
791                  GTK_FILE_SYSTEM_ERROR_FAILED,
792                  _("Could not get a stock icon for %s"),
793                  stock_name);
794
795   return pixbuf;
796 }
797
798 static GdkPixbuf *
799 gtk_file_system_unix_volume_render_icon (GtkFileSystem        *file_system,
800                                          GtkFileSystemVolume  *volume,
801                                          GtkWidget            *widget,
802                                          gint                  pixel_size,
803                                          GError              **error)
804 {
805   GdkPixbuf *pixbuf;
806
807   pixbuf = get_cached_icon (widget, "gnome-fs-blockdev", pixel_size);
808   if (pixbuf)
809     return pixbuf;
810
811   pixbuf = get_fallback_icon (widget, ICON_BLOCK_DEVICE, error);
812   g_assert (pixbuf != NULL);
813
814   return pixbuf;
815 }
816
817 static char *
818 get_parent_dir (const char *filename)
819 {
820   int len;
821
822   len = strlen (filename);
823
824   /* Ignore trailing slashes */
825   if (len > 1 && filename[len - 1] == '/')
826     {
827       char *tmp, *parent;
828
829       tmp = g_strndup (filename, len - 1);
830
831       parent = g_path_get_dirname (tmp);
832       g_free (tmp);
833
834       return parent;
835     }
836   else
837     return g_path_get_dirname (filename);
838 }
839
840 static gboolean
841 gtk_file_system_unix_get_parent (GtkFileSystem     *file_system,
842                                  const GtkFilePath *path,
843                                  GtkFilePath      **parent,
844                                  GError           **error)
845 {
846   const char *filename;
847
848   filename = gtk_file_path_get_string (path);
849   g_return_val_if_fail (filename != NULL, FALSE);
850   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
851
852   if (filename_is_root (filename))
853     {
854       *parent = NULL;
855     }
856   else
857     {
858       gchar *parent_filename = get_parent_dir (filename);
859       *parent = filename_to_path (parent_filename);
860       g_free (parent_filename);
861     }
862
863   return TRUE;
864 }
865
866 static GtkFilePath *
867 gtk_file_system_unix_make_path (GtkFileSystem    *file_system,
868                                const GtkFilePath *base_path,
869                                const gchar       *display_name,
870                                GError           **error)
871 {
872   const char *base_filename;
873   gchar *filename;
874   gchar *full_filename;
875   GError *tmp_error = NULL;
876   GtkFilePath *result;
877
878   base_filename = gtk_file_path_get_string (base_path);
879   g_return_val_if_fail (base_filename != NULL, NULL);
880   g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
881
882   if (strchr (display_name, G_DIR_SEPARATOR))
883     {
884       g_set_error (error,
885                    GTK_FILE_SYSTEM_ERROR,
886                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
887                    _("The name \"%s\" is not valid because it contains the character \"%s\". "
888                      "Please use a different name."),
889                    display_name,
890                    G_DIR_SEPARATOR_S);
891       return NULL;
892     }
893
894   filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
895   if (!filename)
896     {
897       g_set_error (error,
898                    GTK_FILE_SYSTEM_ERROR,
899                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
900                    "%s",
901                    tmp_error->message);
902
903       g_error_free (tmp_error);
904
905       return NULL;
906     }
907
908   full_filename = g_build_filename (base_filename, filename, NULL);
909   result = filename_to_path (full_filename);
910   g_free (filename);
911   g_free (full_filename);
912
913   return result;
914 }
915
916 /* If this was a publically exported function, it should return
917  * a dup'ed result, but we make it modify-in-place for efficiency
918  * here, and because it works for us.
919  */
920 static void
921 canonicalize_filename (gchar *filename)
922 {
923   gchar *p, *q;
924   gboolean last_was_slash = FALSE;
925
926   p = filename;
927   q = filename;
928
929   while (*p)
930     {
931       if (*p == G_DIR_SEPARATOR)
932         {
933           if (!last_was_slash)
934             *q++ = G_DIR_SEPARATOR;
935
936           last_was_slash = TRUE;
937         }
938       else
939         {
940           if (last_was_slash && *p == '.')
941             {
942               if (*(p + 1) == G_DIR_SEPARATOR ||
943                   *(p + 1) == '\0')
944                 {
945                   if (*(p + 1) == '\0')
946                     break;
947
948                   p += 1;
949                 }
950               else if (*(p + 1) == '.' &&
951                        (*(p + 2) == G_DIR_SEPARATOR ||
952                         *(p + 2) == '\0'))
953                 {
954                   if (q > filename + 1)
955                     {
956                       q--;
957                       while (q > filename + 1 &&
958                              *(q - 1) != G_DIR_SEPARATOR)
959                         q--;
960                     }
961
962                   if (*(p + 2) == '\0')
963                     break;
964
965                   p += 2;
966                 }
967               else
968                 {
969                   *q++ = *p;
970                   last_was_slash = FALSE;
971                 }
972             }
973           else
974             {
975               *q++ = *p;
976               last_was_slash = FALSE;
977             }
978         }
979
980       p++;
981     }
982
983   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
984     q--;
985
986   *q = '\0';
987 }
988
989 /* Takes a user-typed filename and expands a tilde at the beginning of the string */
990 static char *
991 expand_tilde (const char *filename)
992 {
993   const char *notilde;
994   const char *slash;
995   const char *home;
996
997   if (filename[0] != '~')
998     return g_strdup (filename);
999
1000   notilde = filename + 1;
1001
1002   slash = strchr (notilde, G_DIR_SEPARATOR);
1003   if (!slash)
1004     return NULL;
1005
1006   if (slash == notilde)
1007     {
1008       home = g_get_home_dir ();
1009
1010       if (!home)
1011         return g_strdup (filename);
1012     }
1013   else
1014     {
1015       char *username;
1016       struct passwd *passwd;
1017
1018       username = g_strndup (notilde, slash - notilde);
1019       passwd = getpwnam (username);
1020       g_free (username);
1021
1022       if (!passwd)
1023         return g_strdup (filename);
1024
1025       home = passwd->pw_dir;
1026     }
1027
1028   return g_build_filename (home, G_DIR_SEPARATOR_S, slash + 1, NULL);
1029 }
1030
1031 static gboolean
1032 gtk_file_system_unix_parse (GtkFileSystem     *file_system,
1033                             const GtkFilePath *base_path,
1034                             const gchar       *str,
1035                             GtkFilePath      **folder,
1036                             gchar            **file_part,
1037                             GError           **error)
1038 {
1039   const char *base_filename;
1040   gchar *filename;
1041   gchar *last_slash;
1042   gboolean result = FALSE;
1043
1044   base_filename = gtk_file_path_get_string (base_path);
1045   g_return_val_if_fail (base_filename != NULL, FALSE);
1046   g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
1047
1048   filename = expand_tilde (str);
1049   if (!filename)
1050     {
1051       g_set_error (error,
1052                    GTK_FILE_SYSTEM_ERROR,
1053                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1054                    "%s", ""); /* nothing for now, as we are string-frozen */
1055       return FALSE;
1056     }
1057
1058   last_slash = strrchr (filename, G_DIR_SEPARATOR);
1059   if (!last_slash)
1060     {
1061       *folder = gtk_file_path_copy (base_path);
1062       *file_part = g_strdup (filename);
1063       result = TRUE;
1064     }
1065   else
1066     {
1067       gchar *folder_part;
1068       gchar *folder_path;
1069       GError *tmp_error = NULL;
1070
1071       if (last_slash == filename)
1072         folder_part = g_strdup ("/");
1073       else
1074         folder_part = g_filename_from_utf8 (filename, last_slash - filename,
1075                                             NULL, NULL, &tmp_error);
1076
1077       if (!folder_part)
1078         {
1079           g_set_error (error,
1080                        GTK_FILE_SYSTEM_ERROR,
1081                        GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1082                        "%s",
1083                        tmp_error->message);
1084           g_error_free (tmp_error);
1085         }
1086       else
1087         {
1088           if (folder_part[0] == G_DIR_SEPARATOR)
1089             folder_path = folder_part;
1090           else
1091             {
1092               folder_path = g_build_filename (base_filename, folder_part, NULL);
1093               g_free (folder_part);
1094             }
1095
1096           canonicalize_filename (folder_path);
1097
1098           *folder = filename_to_path (folder_path);
1099           *file_part = g_strdup (last_slash + 1);
1100
1101           g_free (folder_path);
1102
1103           result = TRUE;
1104         }
1105     }
1106
1107   g_free (filename);
1108
1109   return result;
1110 }
1111
1112 static gchar *
1113 gtk_file_system_unix_path_to_uri (GtkFileSystem     *file_system,
1114                                   const GtkFilePath *path)
1115 {
1116   return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
1117 }
1118
1119 static gchar *
1120 gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
1121                                        const GtkFilePath *path)
1122 {
1123   return g_strdup (gtk_file_path_get_string (path));
1124 }
1125
1126 static GtkFilePath *
1127 gtk_file_system_unix_uri_to_path (GtkFileSystem     *file_system,
1128                                   const gchar       *uri)
1129 {
1130   GtkFilePath *path;
1131   gchar *filename = g_filename_from_uri (uri, NULL, NULL);
1132
1133   if (filename)
1134     {
1135       path = filename_to_path (filename);
1136       g_free (filename);
1137     }
1138   else
1139     path = NULL;
1140
1141   return path;
1142 }
1143
1144 static GtkFilePath *
1145 gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
1146                                        const gchar   *filename)
1147 {
1148   return filename_to_path (filename);
1149 }
1150
1151 /* Returns the name of the icon to be used for a path which is known to be a
1152  * directory.  This can vary for Home, Desktop, etc.
1153  */
1154 static const char *
1155 get_icon_name_for_directory (const char *path)
1156 {
1157   static char *desktop_path = NULL;
1158
1159   if (!g_get_home_dir ())
1160     return "gnome-fs-directory";
1161
1162   if (!desktop_path)
1163       desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1164
1165   if (strcmp (g_get_home_dir (), path) == 0)
1166     return "gnome-fs-home";
1167   else if (strcmp (desktop_path, path) == 0)
1168     return "gnome-fs-desktop";
1169   else
1170     return "gnome-fs-directory";
1171 }
1172
1173 /* Computes our internal icon type based on a path name; also returns the MIME
1174  * type in case we come up with ICON_REGULAR.
1175  */
1176 static IconType
1177 get_icon_type_from_path (GtkFileSystemUnix *system_unix,
1178                          const GtkFilePath *path,
1179                          const char       **mime_type)
1180 {
1181   const char *filename;
1182   char *dirname;
1183   GtkFileFolderUnix *folder_unix;
1184   IconType icon_type;
1185
1186   filename = gtk_file_path_get_string (path);
1187   dirname = g_path_get_dirname (filename);
1188   folder_unix = g_hash_table_lookup (system_unix->folder_hash, dirname);
1189   g_free (dirname);
1190
1191   *mime_type = NULL;
1192
1193   if (folder_unix && folder_unix->have_stat)
1194     {
1195       char *basename;
1196       struct stat_info_entry *entry;
1197
1198       g_assert (folder_unix->stat_info != NULL);
1199
1200       basename = g_path_get_basename (filename);
1201       entry = g_hash_table_lookup (folder_unix->stat_info, basename);
1202       g_free (basename);
1203       if (entry)
1204         {
1205           if (entry->icon_type == ICON_UNDECIDED)
1206             {
1207               entry->icon_type = get_icon_type_from_stat (&entry->statbuf);
1208               g_assert (entry->icon_type != ICON_UNDECIDED);
1209             }
1210           icon_type = entry->icon_type;
1211           if (icon_type == ICON_REGULAR)
1212             {
1213               fill_in_mime_type (folder_unix);
1214               *mime_type = entry->mime_type;
1215             }
1216
1217           return icon_type;
1218         }
1219     }
1220
1221   icon_type = get_icon_type (filename, NULL);
1222   if (icon_type == ICON_REGULAR)
1223     *mime_type = xdg_mime_get_mime_type_for_file (filename);
1224
1225   return icon_type;
1226 }
1227
1228 /* Renders an icon for a non-ICON_REGULAR file */
1229 static GdkPixbuf *
1230 get_special_icon (IconType           icon_type,
1231                   const GtkFilePath *path,
1232                   GtkWidget         *widget,
1233                   gint               pixel_size)
1234 {
1235   const char *name;
1236
1237   g_assert (icon_type != ICON_REGULAR);
1238
1239   switch (icon_type)
1240     {
1241     case ICON_BLOCK_DEVICE:
1242       name = "gnome-fs-blockdev";
1243       break;
1244     case ICON_BROKEN_SYMBOLIC_LINK:
1245       name = "gnome-fs-symlink";
1246       break;
1247     case ICON_CHARACTER_DEVICE:
1248       name = "gnome-fs-chardev";
1249       break;
1250     case ICON_DIRECTORY: {
1251       const char *filename;
1252
1253       filename = gtk_file_path_get_string (path);
1254       name = get_icon_name_for_directory (filename);
1255       break;
1256       }
1257     case ICON_EXECUTABLE:
1258       name ="gnome-fs-executable";
1259       break;
1260     case ICON_FIFO:
1261       name = "gnome-fs-fifo";
1262       break;
1263     case ICON_SOCKET:
1264       name = "gnome-fs-socket";
1265       break;
1266     default:
1267       g_assert_not_reached ();
1268       return NULL;
1269     }
1270
1271   return get_cached_icon (widget, name, pixel_size);
1272 }
1273
1274 static GdkPixbuf *
1275 get_icon_for_mime_type (GtkWidget  *widget,
1276                         const char *mime_type,
1277                         gint        pixel_size)
1278 {
1279   const char *separator;
1280   GString *icon_name;
1281   GdkPixbuf *pixbuf;
1282
1283   separator = strchr (mime_type, '/');
1284   if (!separator)
1285     return NULL; /* maybe we should return a GError with "invalid MIME-type" */
1286
1287   icon_name = g_string_new ("gnome-mime-");
1288   g_string_append_len (icon_name, mime_type, separator - mime_type);
1289   g_string_append_c (icon_name, '-');
1290   g_string_append (icon_name, separator + 1);
1291   pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1292   g_string_free (icon_name, TRUE);
1293   if (pixbuf)
1294     return pixbuf;
1295
1296   icon_name = g_string_new ("gnome-mime-");
1297   g_string_append_len (icon_name, mime_type, separator - mime_type);
1298   pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1299   g_string_free (icon_name, TRUE);
1300
1301   return pixbuf;
1302 }
1303
1304 static GdkPixbuf *
1305 gtk_file_system_unix_render_icon (GtkFileSystem     *file_system,
1306                                   const GtkFilePath *path,
1307                                   GtkWidget         *widget,
1308                                   gint               pixel_size,
1309                                   GError           **error)
1310 {
1311   GtkFileSystemUnix *system_unix;
1312   IconType icon_type;
1313   const char *mime_type;
1314   GdkPixbuf *pixbuf;
1315
1316   system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
1317
1318   icon_type = get_icon_type_from_path (system_unix, path, &mime_type);
1319
1320   switch (icon_type) {
1321   case ICON_NONE:
1322     goto fallback;
1323
1324   case ICON_REGULAR:
1325     pixbuf = get_icon_for_mime_type (widget, mime_type, pixel_size);
1326     break;
1327
1328   default:
1329     pixbuf = get_special_icon (icon_type, path, widget, pixel_size);
1330   }
1331
1332   if (pixbuf)
1333     goto out;
1334
1335  fallback:
1336
1337   pixbuf = get_cached_icon (widget, "gnome-fs-regular", pixel_size);
1338   if (pixbuf)
1339     goto out;
1340
1341   pixbuf = get_fallback_icon (widget, icon_type, error);
1342
1343  out:
1344
1345   return pixbuf;
1346 }
1347
1348 static void
1349 bookmark_list_free (GSList *list)
1350 {
1351   GSList *l;
1352
1353   for (l = list; l; l = l->next)
1354     g_free (l->data);
1355
1356   g_slist_free (list);
1357 }
1358
1359 /* Returns whether a URI is a local file:// */
1360 static gboolean
1361 is_local_uri (const char *uri)
1362 {
1363   char *filename;
1364   char *hostname;
1365   gboolean result;
1366
1367   /* This is rather crude, but hey */
1368   filename = g_filename_from_uri (uri, &hostname, NULL);
1369
1370   result = (filename && !hostname);
1371
1372   g_free (filename);
1373   g_free (hostname);
1374
1375   return result;
1376 }
1377
1378 static char *
1379 bookmark_get_filename (void)
1380 {
1381   char *filename;
1382
1383   filename = g_build_filename (g_get_home_dir (), 
1384                                BOOKMARKS_FILENAME, NULL);
1385   g_assert (filename != NULL);
1386   return filename;
1387 }
1388
1389 static gboolean
1390 bookmark_list_read (GSList **bookmarks, GError **error)
1391 {
1392   gchar *filename;
1393   gchar *contents;
1394   gboolean result = FALSE;
1395
1396   filename = bookmark_get_filename ();
1397   *bookmarks = NULL;
1398
1399   if (g_file_get_contents (filename, &contents, NULL, error))
1400     {
1401       gchar **lines = g_strsplit (contents, "\n", -1);
1402       int i;
1403       GHashTable *table;
1404
1405       table = g_hash_table_new (g_str_hash, g_str_equal);
1406
1407       for (i = 0; lines[i]; i++)
1408         {
1409           if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
1410             {
1411               *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
1412               g_hash_table_insert (table, lines[i], lines[i]);
1413             }
1414         }
1415
1416       g_free (contents);
1417       g_hash_table_destroy (table);
1418       g_strfreev (lines);
1419
1420       *bookmarks = g_slist_reverse (*bookmarks);
1421       result = TRUE;
1422     }
1423
1424   g_free (filename);
1425
1426   return result;
1427 }
1428
1429 static gboolean
1430 bookmark_list_write (GSList  *bookmarks, 
1431                      GError **error)
1432 {
1433   GSList *l;
1434   GString *string;
1435   char *filename;
1436   GError *tmp_error = NULL;
1437   gboolean result;
1438
1439   string = g_string_new ("");
1440
1441   for (l = bookmarks; l; l = l->next)
1442     {
1443       g_string_append (string, l->data);
1444       g_string_append_c (string, '\n');
1445     }
1446
1447   filename = bookmark_get_filename ();
1448
1449   result = g_file_set_contents (filename, string->str, -1, &tmp_error);
1450
1451   g_free (filename);
1452   g_string_free (string, TRUE);
1453
1454   if (!result)
1455     {
1456       g_set_error (error,
1457                    GTK_FILE_SYSTEM_ERROR,
1458                    GTK_FILE_SYSTEM_ERROR_FAILED,
1459                    _("Bookmark saving failed: %s"),
1460                    tmp_error->message);
1461       
1462       g_error_free (tmp_error);
1463     }
1464
1465   return result;
1466 }
1467
1468 static gboolean
1469 gtk_file_system_unix_insert_bookmark (GtkFileSystem     *file_system,
1470                                       const GtkFilePath *path,
1471                                       gint               position,
1472                                       GError           **error)
1473 {
1474   GSList *bookmarks;
1475   int num_bookmarks;
1476   GSList *l;
1477   char *uri;
1478   gboolean result;
1479   GError *err;
1480
1481   err = NULL;
1482   if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
1483     {
1484       g_propagate_error (error, err);
1485       return FALSE;
1486     }
1487
1488   num_bookmarks = g_slist_length (bookmarks);
1489   g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
1490
1491   result = FALSE;
1492
1493   uri = gtk_file_system_unix_path_to_uri (file_system, path);
1494
1495   for (l = bookmarks; l; l = l->next)
1496     {
1497       char *bookmark, *space;
1498
1499       bookmark = l->data;
1500       
1501       space = strchr (bookmark, ' ');
1502       if (space)
1503         *space = '\0';
1504       if (strcmp (bookmark, uri) != 0)
1505         {
1506           if (space)
1507             *space = ' ';
1508         }
1509       else
1510         {
1511           g_set_error (error,
1512                        GTK_FILE_SYSTEM_ERROR,
1513                        GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
1514                        _("'%s' already exists in the bookmarks list"),
1515                        uri);
1516           goto out;
1517         }
1518     }
1519
1520   bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
1521   if (bookmark_list_write (bookmarks, error))
1522     {
1523       result = TRUE;
1524       g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1525     }
1526
1527  out:
1528
1529   g_free (uri);
1530   bookmark_list_free (bookmarks);
1531
1532   return result;
1533 }
1534
1535 static gboolean
1536 gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
1537                                       const GtkFilePath *path,
1538                                       GError           **error)
1539 {
1540   GSList *bookmarks;
1541   char *uri;
1542   GSList *l;
1543   gboolean result;
1544
1545   if (!bookmark_list_read (&bookmarks, error))
1546     return FALSE;
1547
1548   result = FALSE;
1549
1550   uri = gtk_file_system_path_to_uri (file_system, path);
1551
1552   for (l = bookmarks; l; l = l->next)
1553     {
1554       char *bookmark, *space;
1555
1556       bookmark = (char *)l->data;
1557       space = strchr (bookmark, ' ');
1558       if (space)
1559         *space = '\0';
1560
1561       if (strcmp (bookmark, uri) != 0)
1562         {
1563           if (space)
1564             *space = ' ';
1565         }
1566       else
1567         {
1568           g_free (l->data);
1569           bookmarks = g_slist_remove_link (bookmarks, l);
1570           g_slist_free_1 (l);
1571
1572           if (bookmark_list_write (bookmarks, error))
1573             {
1574               result = TRUE;
1575
1576               g_signal_emit_by_name (file_system, "bookmarks-changed", 0);            
1577             }
1578
1579           goto out;
1580         }
1581     }
1582
1583   g_set_error (error,
1584                GTK_FILE_SYSTEM_ERROR,
1585                GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1586                _("'%s' does not exist in the bookmarks list"),
1587                uri);
1588
1589  out:
1590
1591   g_free (uri);
1592   bookmark_list_free (bookmarks);
1593
1594   return result;
1595 }
1596
1597 static GSList *
1598 gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
1599 {
1600   GSList *bookmarks;
1601   GSList *result;
1602   GSList *l;
1603
1604   if (!bookmark_list_read (&bookmarks, NULL))
1605     return NULL;
1606
1607   result = NULL;
1608
1609   for (l = bookmarks; l; l = l->next)
1610     {
1611       char *bookmark, *space;
1612
1613       bookmark = (char *)l->data;
1614       space = strchr (bookmark, ' ');
1615       if (space)
1616         *space = '\0';
1617
1618       if (is_local_uri (bookmark))
1619         result = g_slist_prepend (result, gtk_file_system_unix_uri_to_path (file_system, bookmark));
1620     }
1621
1622   bookmark_list_free (bookmarks);
1623
1624   result = g_slist_reverse (result);
1625   return result;
1626 }
1627
1628 static gchar *
1629 gtk_file_system_unix_get_bookmark_label (GtkFileSystem     *file_system,
1630                                          const GtkFilePath *path)
1631 {
1632   GSList *labels;
1633   gchar *label;
1634   GSList *l;
1635   char *bookmark, *space, *uri;
1636   
1637   labels = NULL;
1638   label = NULL;
1639
1640   uri = gtk_file_system_path_to_uri (file_system, path);
1641   bookmark_list_read (&labels, NULL);
1642
1643   for (l = labels; l && !label; l = l->next) 
1644     {
1645       bookmark = (char *)l->data;
1646       space = strchr (bookmark, ' ');
1647       if (!space)
1648         continue;
1649
1650       *space = '\0';
1651
1652       if (strcmp (uri, bookmark) == 0)
1653         label = g_strdup (space + 1);
1654     }
1655
1656   bookmark_list_free (labels);
1657   g_free (uri);
1658
1659   return label;
1660 }
1661
1662 static void
1663 gtk_file_system_unix_set_bookmark_label (GtkFileSystem     *file_system,
1664                                          const GtkFilePath *path,
1665                                          const gchar       *label)
1666 {
1667   GSList *labels;
1668   GSList *l;
1669   gchar *bookmark, *space, *uri;
1670   gboolean found;
1671
1672   labels = NULL;
1673
1674   uri = gtk_file_system_path_to_uri (file_system, path);
1675   bookmark_list_read (&labels, NULL);
1676
1677   found = FALSE;
1678   for (l = labels; l && !found; l = l->next) 
1679     {
1680       bookmark = (gchar *)l->data;
1681       space = strchr (bookmark, ' ');
1682       if (space)
1683         *space = '\0';
1684
1685       if (strcmp (bookmark, uri) != 0)
1686         {
1687           if (space)
1688             *space = ' ';
1689         }
1690       else
1691         {
1692           g_free (bookmark);
1693           
1694           if (label && *label)
1695             l->data = g_strdup_printf ("%s %s", uri, label);
1696           else
1697             l->data = g_strdup (uri);
1698
1699           found = TRUE;
1700           break;
1701         }
1702     }
1703
1704   if (found)
1705     {
1706       if (bookmark_list_write (labels, NULL))
1707         g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1708     }
1709   
1710   bookmark_list_free (labels);
1711   g_free (uri);
1712 }
1713
1714 /*
1715  * GtkFileFolderUnix
1716  */
1717 static GType
1718 gtk_file_folder_unix_get_type (void)
1719 {
1720   static GType file_folder_unix_type = 0;
1721
1722   if (!file_folder_unix_type)
1723     {
1724       static const GTypeInfo file_folder_unix_info =
1725       {
1726         sizeof (GtkFileFolderUnixClass),
1727         NULL,           /* base_init */
1728         NULL,           /* base_finalize */
1729         (GClassInitFunc) gtk_file_folder_unix_class_init,
1730         NULL,           /* class_finalize */
1731         NULL,           /* class_data */
1732         sizeof (GtkFileFolderUnix),
1733         0,              /* n_preallocs */
1734         (GInstanceInitFunc) gtk_file_folder_unix_init,
1735       };
1736
1737       static const GInterfaceInfo file_folder_info =
1738       {
1739         (GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
1740         NULL,                                                 /* interface_finalize */
1741         NULL                                                  /* interface_data */
1742       };
1743
1744       file_folder_unix_type = g_type_register_static (G_TYPE_OBJECT,
1745                                                       "GtkFileFolderUnix",
1746                                                       &file_folder_unix_info, 0);
1747       g_type_add_interface_static (file_folder_unix_type,
1748                                    GTK_TYPE_FILE_FOLDER,
1749                                    &file_folder_info);
1750     }
1751
1752   return file_folder_unix_type;
1753 }
1754
1755 static void
1756 gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
1757 {
1758   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1759
1760   folder_parent_class = g_type_class_peek_parent (class);
1761
1762   gobject_class->finalize = gtk_file_folder_unix_finalize;
1763 }
1764
1765 static void
1766 gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
1767 {
1768   iface->get_info = gtk_file_folder_unix_get_info;
1769   iface->list_children = gtk_file_folder_unix_list_children;
1770   iface->is_finished_loading = gtk_file_folder_unix_is_finished_loading;
1771 }
1772
1773 static void
1774 gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
1775 {
1776 }
1777
1778 static void
1779 gtk_file_folder_unix_finalize (GObject *object)
1780 {
1781   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
1782
1783   g_hash_table_remove (folder_unix->system_unix->folder_hash, folder_unix->filename);
1784
1785   if (folder_unix->stat_info)
1786     {
1787 #if 0
1788       g_print ("Releasing information for directory %s\n", folder_unix->filename);
1789 #endif
1790       g_hash_table_destroy (folder_unix->stat_info);
1791     }
1792
1793   g_free (folder_unix->filename);
1794
1795   folder_parent_class->finalize (object);
1796 }
1797
1798 /* Creates a GtkFileInfo for "/" by stat()ing it */
1799 static GtkFileInfo *
1800 file_info_for_root_with_error (const char  *root_name,
1801                                GError     **error)
1802 {
1803   struct stat statbuf;
1804   GtkFileInfo *info;
1805
1806   if (stat (root_name, &statbuf) != 0)
1807     {
1808       int saved_errno;
1809
1810       saved_errno = errno; 
1811       g_set_error (error,
1812                    GTK_FILE_SYSTEM_ERROR,
1813                    GTK_FILE_SYSTEM_ERROR_FAILED,
1814                    _("Error getting information for '/': %s"),
1815                    g_strerror (saved_errno));
1816
1817       return NULL;
1818     }
1819
1820   info = gtk_file_info_new ();
1821   gtk_file_info_set_display_name (info, "/");
1822   gtk_file_info_set_is_folder (info, TRUE);
1823   gtk_file_info_set_is_hidden (info, FALSE);
1824   gtk_file_info_set_mime_type (info, "x-directory/normal");
1825   gtk_file_info_set_modification_time (info, statbuf.st_mtime);
1826   gtk_file_info_set_size (info, statbuf.st_size);
1827
1828   return info;
1829 }
1830
1831 static gboolean
1832 stat_with_error (const char   *filename,
1833                  struct stat  *statbuf,
1834                  GError      **error)
1835 {
1836   if (stat (filename, statbuf) == -1 &&
1837       (errno != ENOENT || lstat (filename, statbuf) == -1))
1838     {
1839       int saved_errno;
1840       int code;
1841       char *display_name;
1842
1843       saved_errno = errno;
1844
1845       if (saved_errno == ENOENT)
1846         code = GTK_FILE_SYSTEM_ERROR_NONEXISTENT;
1847       else
1848         code = GTK_FILE_SYSTEM_ERROR_FAILED;
1849
1850       display_name = g_filename_display_name (filename);
1851       g_set_error (error,
1852                    GTK_FILE_SYSTEM_ERROR,
1853                    code,
1854                    _("Error getting information for '%s': %s"),
1855                    display_name,
1856                    g_strerror (saved_errno));
1857
1858       g_free (display_name);
1859       return FALSE;
1860     }
1861
1862   return TRUE;
1863 }
1864
1865 /* Creates a new GtkFileInfo from the specified data */
1866 static GtkFileInfo *
1867 create_file_info (GtkFileFolderUnix *folder_unix,
1868                   const char        *filename,
1869                   const char        *basename,
1870                   GtkFileInfoType    types,
1871                   struct stat       *statbuf,
1872                   const char        *mime_type)
1873 {
1874   GtkFileInfo *info;
1875
1876   info = gtk_file_info_new ();
1877   
1878   if (types & GTK_FILE_INFO_DISPLAY_NAME)
1879     {
1880       gchar *display_name = g_filename_display_basename (filename);
1881       gtk_file_info_set_display_name (info, display_name);
1882       g_free (display_name);
1883     }
1884
1885   if (types & GTK_FILE_INFO_IS_HIDDEN)
1886     {
1887       if (file_is_hidden (folder_unix, basename))
1888         gtk_file_info_set_is_hidden (info, TRUE);
1889     }
1890
1891   if (types & GTK_FILE_INFO_IS_FOLDER)
1892     gtk_file_info_set_is_folder (info, S_ISDIR (statbuf->st_mode));
1893
1894   if (types & GTK_FILE_INFO_MIME_TYPE)
1895     gtk_file_info_set_mime_type (info, mime_type);
1896
1897   if (types & GTK_FILE_INFO_MODIFICATION_TIME)
1898     gtk_file_info_set_modification_time (info, statbuf->st_mtime);
1899
1900   if (types & GTK_FILE_INFO_SIZE)
1901     gtk_file_info_set_size (info, (gint64) statbuf->st_size);
1902
1903   return info;
1904 }
1905
1906 static struct stat_info_entry *
1907 create_stat_info_entry_and_emit_add (GtkFileFolderUnix *folder_unix,
1908                                      const char        *filename,
1909                                      const char        *basename,
1910                                      struct stat       *statbuf)
1911 {
1912   GSList *paths;
1913   GtkFilePath *path;
1914   struct stat_info_entry *entry;
1915
1916   entry = g_new0 (struct stat_info_entry, 1);
1917
1918   if ((folder_unix->types & STAT_NEEDED_MASK) != 0)
1919     entry->statbuf = *statbuf;
1920
1921   if ((folder_unix->types & GTK_FILE_INFO_MIME_TYPE) != 0)
1922     entry->mime_type = g_strdup (xdg_mime_get_mime_type_for_file (filename));
1923
1924   g_hash_table_insert (folder_unix->stat_info,
1925                        g_strdup (basename),
1926                        entry);
1927
1928   path = gtk_file_path_new_dup (filename);
1929   paths = g_slist_append (NULL, path);
1930   g_signal_emit_by_name (folder_unix, "files-added", paths);
1931   gtk_file_path_free (path);
1932   g_slist_free (paths);
1933
1934   return entry;
1935 }
1936
1937 static GtkFileInfo *
1938 gtk_file_folder_unix_get_info (GtkFileFolder      *folder,
1939                                const GtkFilePath  *path,
1940                                GError            **error)
1941 {
1942   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1943   GtkFileInfo *info;
1944   gchar *dirname, *basename;
1945   const char *filename;
1946   GtkFileInfoType types;
1947   struct stat statbuf;
1948   const char *mime_type;
1949
1950   /* Get_info for "/" */
1951   if (!path)
1952     {
1953       g_return_val_if_fail (filename_is_root (folder_unix->filename), NULL);
1954       return file_info_for_root_with_error (folder_unix->filename, error);
1955     }
1956
1957   /* Get_info for normal files */
1958
1959   filename = gtk_file_path_get_string (path);
1960   g_return_val_if_fail (filename != NULL, NULL);
1961   g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1962
1963   dirname = get_parent_dir (filename);
1964   g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
1965   g_free (dirname);
1966
1967   basename = g_path_get_basename (filename);
1968   types = folder_unix->types;
1969
1970   if (folder_unix->have_stat)
1971     {
1972       struct stat_info_entry *entry;
1973
1974       g_assert (folder_unix->stat_info != NULL);
1975       entry = g_hash_table_lookup (folder_unix->stat_info, basename);
1976
1977       if (!entry)
1978         {
1979           if (!stat_with_error (filename, &statbuf, error))
1980             {
1981               g_free (basename);
1982               return NULL;
1983             }
1984
1985           entry = create_stat_info_entry_and_emit_add (folder_unix, filename, basename, &statbuf);
1986         }
1987
1988       info = create_file_info (folder_unix, filename, basename, types, &entry->statbuf, entry->mime_type);
1989       g_free (basename);
1990       return info;
1991     }
1992   else
1993     {
1994       if (!stat_with_error (filename, &statbuf, error))
1995         {
1996           g_free (basename);
1997           return NULL;
1998         }
1999
2000       if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
2001         mime_type = xdg_mime_get_mime_type_for_file (filename);
2002       else
2003         mime_type = NULL;
2004
2005       info = create_file_info (folder_unix, filename, basename, types, &statbuf, mime_type);
2006       g_free (basename);
2007       return info;
2008     }
2009 }
2010
2011
2012 static void
2013 cb_list_children (gpointer key, gpointer value, gpointer user_data)
2014 {
2015   GSList **children = user_data;
2016   *children = g_slist_prepend (*children, key);
2017 }
2018
2019 static gboolean
2020 gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
2021                                     GSList        **children,
2022                                     GError        **error)
2023 {
2024   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
2025   GSList *l;
2026
2027   if (!fill_in_names (folder_unix, error))
2028     return FALSE;
2029
2030   *children = NULL;
2031
2032   /* Get the list of basenames.  */
2033   g_hash_table_foreach (folder_unix->stat_info, cb_list_children, children);
2034
2035   /* Turn basenames into GFilePaths.  */
2036   for (l = *children; l; l = l->next)
2037     {
2038       const char *basename = l->data;
2039       char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
2040       l->data = filename_to_path (fullname);
2041       g_free (fullname);
2042     }
2043
2044   return TRUE;
2045 }
2046
2047 static gboolean
2048 gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder)
2049 {
2050   /* Since we don't do asynchronous loads, we are always finished loading */
2051   return TRUE;
2052 }
2053
2054 static void
2055 free_stat_info_entry (struct stat_info_entry *entry)
2056 {
2057   g_free (entry->mime_type);
2058   g_free (entry);
2059 }
2060
2061 static gboolean
2062 fill_in_names (GtkFileFolderUnix *folder_unix, GError **error)
2063 {
2064   GDir *dir;
2065
2066   if (folder_unix->stat_info)
2067     return TRUE;
2068
2069   dir = g_dir_open (folder_unix->filename, 0, error);
2070   if (!dir)
2071     return FALSE;
2072
2073   folder_unix->stat_info = g_hash_table_new_full (g_str_hash, g_str_equal,
2074                                                   (GDestroyNotify) g_free,
2075                                                   (GDestroyNotify) free_stat_info_entry);
2076
2077   while (TRUE)
2078     {
2079       struct stat_info_entry *entry;
2080       const gchar *basename;
2081
2082       basename = g_dir_read_name (dir);
2083       if (!basename)
2084         break;
2085
2086       entry = g_new0 (struct stat_info_entry, 1);
2087       if (folder_unix->is_network_dir)
2088         {
2089           entry->statbuf.st_mode = S_IFDIR;
2090           entry->mime_type = g_strdup ("x-directory/normal");
2091         }
2092
2093       g_hash_table_insert (folder_unix->stat_info,
2094                            g_strdup (basename),
2095                            entry);
2096     }
2097
2098   g_dir_close (dir);
2099
2100   folder_unix->asof = time (NULL);
2101   return TRUE;
2102 }
2103
2104 static gboolean
2105 cb_fill_in_stats (gpointer key, gpointer value, gpointer user_data)
2106 {
2107   const char *basename = key;
2108   struct stat_info_entry *entry = value;
2109   GtkFileFolderUnix *folder_unix = user_data;
2110   char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
2111   gboolean result;
2112
2113   if (stat (fullname, &entry->statbuf) == -1 &&
2114       (errno != ENOENT || lstat (fullname, &entry->statbuf) == -1))
2115     result = TRUE;  /* Couldn't stat -- remove from hash.  */
2116   else
2117     result = FALSE;
2118
2119   g_free (fullname);
2120   return result;
2121 }
2122
2123
2124 static void
2125 fill_in_stats (GtkFileFolderUnix *folder_unix)
2126 {
2127   if (folder_unix->have_stat)
2128     return;
2129
2130   if (!fill_in_names (folder_unix, NULL))
2131     return;
2132
2133   if (!folder_unix->is_network_dir)
2134     g_hash_table_foreach_remove (folder_unix->stat_info,
2135                                  cb_fill_in_stats,
2136                                  folder_unix);
2137
2138   folder_unix->have_stat = TRUE;
2139 }
2140
2141
2142 static gboolean
2143 cb_fill_in_mime_type (gpointer key, gpointer value, gpointer user_data)
2144 {
2145   const char *basename = key;
2146   struct stat_info_entry *entry = value;
2147   GtkFileFolderUnix *folder_unix = user_data;
2148   char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
2149
2150   /* FIXME: Should not need to re-stat.  */
2151   const char *mime_type = xdg_mime_get_mime_type_for_file (fullname);
2152   entry->mime_type = g_strdup (mime_type);
2153
2154   g_free (fullname);
2155   /* FIXME: free on NULL?  */
2156   return FALSE;
2157 }
2158
2159 static void
2160 fill_in_mime_type (GtkFileFolderUnix *folder_unix)
2161 {
2162   if (folder_unix->have_mime_type)
2163     return;
2164
2165   if (!folder_unix->have_stat)
2166     return;
2167
2168   g_assert (folder_unix->stat_info != NULL);
2169
2170   if (!folder_unix->is_network_dir)
2171     g_hash_table_foreach_remove (folder_unix->stat_info,
2172                                  cb_fill_in_mime_type,
2173                                  folder_unix);
2174
2175   folder_unix->have_mime_type = TRUE;
2176 }
2177
2178 static void
2179 fill_in_hidden (GtkFileFolderUnix *folder_unix)
2180 {
2181   gchar *hidden_file;
2182   gchar *contents;
2183   
2184   if (folder_unix->have_hidden)
2185     return;
2186
2187   hidden_file = g_build_filename (folder_unix->filename, HIDDEN_FILENAME, NULL);
2188
2189   if (g_file_get_contents (hidden_file, &contents, NULL, NULL))
2190     {
2191       gchar **lines; 
2192       int i;
2193       
2194       lines = g_strsplit (contents, "\n", -1);
2195       for (i = 0; lines[i]; i++)
2196         {
2197           if (lines[i][0])
2198             {
2199               struct stat_info_entry *entry;
2200               
2201               entry = g_hash_table_lookup (folder_unix->stat_info, lines[i]);
2202               if (entry != NULL)
2203                 entry->hidden = TRUE;
2204             }
2205         }
2206       
2207       g_strfreev (lines);
2208       g_free (contents);
2209     }
2210
2211   g_free (hidden_file);
2212   folder_unix->have_hidden = TRUE;
2213 }
2214
2215 static GtkFilePath *
2216 filename_to_path (const char *filename)
2217 {
2218   char *tmp;
2219
2220   tmp = remove_trailing_slash (filename);
2221   return gtk_file_path_new_steal (tmp);
2222 }
2223
2224 static gboolean
2225 filename_is_root (const char *filename)
2226 {
2227   const gchar *after_root;
2228
2229   after_root = g_path_skip_root (filename);
2230
2231   return (after_root != NULL && *after_root == '\0');
2232 }
2233
2234 static gboolean
2235 file_is_hidden (GtkFileFolderUnix *folder_unix,
2236                 const char        *basename)
2237 {
2238  struct stat_info_entry *entry;
2239
2240   if (basename[0] == '.' || basename[strlen (basename) - 1] == '~')
2241     return TRUE;
2242  
2243   if (folder_unix->have_stat)
2244     {
2245       fill_in_hidden (folder_unix);
2246       
2247       entry = g_hash_table_lookup (folder_unix->stat_info, basename);
2248   
2249       if (entry)
2250         return entry->hidden;
2251     }
2252
2253   return FALSE;
2254 }
2255
2256 #define __GTK_FILE_SYSTEM_UNIX_C__
2257 #include "gtkaliasdef.c"