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