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