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