]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemunix.c
Use bookmark labels in the combobox.
[~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_idle (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_idle (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_idle (gpointer data)
704 {
705   GSList *l;
706   gboolean unref_file_system = TRUE;
707   GtkFileSystemUnix *system_unix = GTK_FILE_SYSTEM_UNIX (data);
708
709   GDK_THREADS_ENTER ();
710
711   if (!system_unix->execute_callbacks_idle_id)
712     unref_file_system = FALSE;
713   else
714     g_object_ref (system_unix);
715
716   for (l = system_unix->callbacks; l; l = l->next)
717     {
718       struct callback_info *info = l->data;
719
720       switch (info->type)
721         {
722           case CALLBACK_GET_INFO:
723             dispatch_get_info_callback (info->info.get_info);
724             break;
725
726           case CALLBACK_GET_FOLDER:
727             dispatch_get_folder_callback (info->info.get_folder);
728             break;
729
730           case CALLBACK_CREATE_FOLDER:
731             dispatch_create_folder_callback (info->info.create_folder);
732             break;
733
734           case CALLBACK_VOLUME_MOUNT:
735             dispatch_volume_mount_callback (info->info.volume_mount);
736             break;
737         }
738
739       g_free (info);
740     }
741
742   g_slist_free (system_unix->callbacks);
743   system_unix->callbacks = NULL;
744
745   if (unref_file_system)
746     g_object_unref (system_unix);
747
748   system_unix->execute_callbacks_idle_id = 0;
749
750   GDK_THREADS_LEAVE ();
751
752   return FALSE;
753 }
754
755 static void
756 queue_callback (GtkFileSystemUnix   *system_unix,
757                 enum callback_types  type,
758                 gpointer             data)
759 {
760   struct callback_info *info;
761
762   info = g_new (struct callback_info, 1);
763   info->type = type;
764
765   switch (type)
766     {
767       case CALLBACK_GET_INFO:
768         info->info.get_info = data;
769         break;
770
771       case CALLBACK_GET_FOLDER:
772         info->info.get_folder = data;
773         break;
774
775       case CALLBACK_CREATE_FOLDER:
776         info->info.create_folder = data;
777         break;
778
779       case CALLBACK_VOLUME_MOUNT:
780         info->info.volume_mount = data;
781         break;
782     }
783
784   system_unix->callbacks = g_slist_append (system_unix->callbacks, info);
785
786   if (!system_unix->execute_callbacks_idle_id)
787     system_unix->execute_callbacks_idle_id = g_idle_add (execute_callbacks_idle, system_unix);
788 }
789
790 static GtkFileSystemHandle *
791 create_handle (GtkFileSystem *file_system)
792 {
793   GtkFileSystemUnix *system_unix;
794   GtkFileSystemHandle *handle;
795
796   system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
797
798   handle = g_object_new (GTK_TYPE_FILE_SYSTEM_HANDLE_UNIX, NULL);
799   handle->file_system = file_system;
800
801   g_assert (g_hash_table_lookup (system_unix->handles, handle) == NULL);
802   g_hash_table_insert (system_unix->handles, handle, handle);
803
804   return handle;
805 }
806
807
808
809 static GtkFileSystemHandle *
810 gtk_file_system_unix_get_info (GtkFileSystem                *file_system,
811                                const GtkFilePath            *path,
812                                GtkFileInfoType               types,
813                                GtkFileSystemGetInfoCallback  callback,
814                                gpointer                      data)
815 {
816   GError *error = NULL;
817   GtkFileSystemUnix *system_unix;
818   GtkFileSystemHandle *handle;
819   const char *filename;
820   GtkFileInfo *info;
821   gchar *basename;
822   struct stat statbuf;
823   const char *mime_type;
824
825   system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
826   handle = create_handle (file_system);
827
828   filename = gtk_file_path_get_string (path);
829   g_return_val_if_fail (filename != NULL, FALSE);
830   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
831
832   if (!stat_with_error (filename, &statbuf, &error))
833     {
834       g_object_ref (handle);
835       queue_get_info_callback (callback, handle, NULL, error, data);
836       return handle;
837     }
838
839   if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
840     mime_type = xdg_mime_get_mime_type_for_file (filename, &statbuf);
841   else
842     mime_type = NULL;
843
844   basename = g_path_get_basename (filename);
845
846   info = create_file_info (NULL, filename, basename, types, &statbuf,
847                            mime_type);
848   g_free (basename);
849   g_object_ref (handle);
850   queue_get_info_callback (callback, handle, info, NULL, data);
851
852   return handle;
853 }
854
855 static gboolean
856 load_folder (gpointer data)
857 {
858   GtkFileFolderUnix *folder_unix = data;
859   GSList *children;
860
861   GDK_THREADS_ENTER ();
862
863   if ((folder_unix->types & STAT_NEEDED_MASK) != 0)
864     fill_in_stats (folder_unix);
865
866   if ((folder_unix->types & GTK_FILE_INFO_MIME_TYPE) != 0)
867     fill_in_mime_type (folder_unix);
868
869   if (gtk_file_folder_unix_list_children (GTK_FILE_FOLDER (folder_unix), &children, NULL))
870     {
871       folder_unix->is_finished_loading = TRUE;
872       g_signal_emit_by_name (folder_unix, "files-added", children);
873       gtk_file_paths_free (children);
874     }
875
876   folder_unix->load_folder_id = 0;
877
878   g_signal_emit_by_name (folder_unix, "finished-loading", 0);
879
880   GDK_THREADS_LEAVE ();
881
882   return FALSE;
883 }
884
885 static GtkFileSystemHandle *
886 gtk_file_system_unix_get_folder (GtkFileSystem                  *file_system,
887                                  const GtkFilePath              *path,
888                                  GtkFileInfoType                 types,
889                                  GtkFileSystemGetFolderCallback  callback,
890                                  gpointer                        data)
891 {
892   GError *error = NULL;
893   GtkFileSystemUnix *system_unix;
894   GtkFileFolderUnix *folder_unix;
895   GtkFileSystemHandle *handle;
896   const char *filename;
897   char *filename_copy;
898   gboolean set_asof = FALSE;
899
900   system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
901
902   filename = gtk_file_path_get_string (path);
903   g_return_val_if_fail (filename != NULL, NULL);
904   g_return_val_if_fail (g_path_is_absolute (filename), NULL);
905
906   handle = create_handle (file_system);
907
908   filename_copy = remove_trailing_slash (filename);
909   folder_unix = g_hash_table_lookup (system_unix->folder_hash, filename_copy);
910
911   if (folder_unix)
912     {
913       g_free (filename_copy);
914       if (folder_unix->stat_info &&
915           time (NULL) - folder_unix->asof >= FOLDER_CACHE_LIFETIME)
916         {
917 #if 0
918           g_print ("Cleaning out cached directory %s\n", filename);
919 #endif
920           g_hash_table_destroy (folder_unix->stat_info);
921           folder_unix->stat_info = NULL;
922           folder_unix->have_mime_type = FALSE;
923           folder_unix->have_stat = FALSE;
924           folder_unix->have_hidden = FALSE;
925           set_asof = TRUE;
926         }
927
928       g_object_ref (folder_unix);
929       folder_unix->types |= types;
930       types = folder_unix->types;
931     }
932   else
933     {
934       struct stat statbuf;
935       int result;
936       int code;
937       int my_errno;
938
939       code = my_errno = 0; /* shut up GCC */
940
941       result = stat (filename, &statbuf);
942
943       if (result == 0)
944         {
945           if (!S_ISDIR (statbuf.st_mode))
946             {
947               result = -1;
948               code = GTK_FILE_SYSTEM_ERROR_NOT_FOLDER;
949               my_errno = ENOTDIR;
950             }
951         }
952       else
953         {
954           my_errno = errno;
955
956           if (my_errno == ENOENT)
957             code = GTK_FILE_SYSTEM_ERROR_NONEXISTENT;
958           else
959             code = GTK_FILE_SYSTEM_ERROR_FAILED;
960         }
961
962       if (result != 0)
963         {
964           gchar *display_name = g_filename_display_name (filename);
965           g_set_error (&error,
966                        GTK_FILE_SYSTEM_ERROR,
967                        code,
968                        _("Error getting information for '%s': %s"),
969                        display_name,
970                        g_strerror (my_errno));
971
972           g_object_ref (handle);
973           queue_get_folder_callback (callback, handle, NULL, error, data);
974
975           g_free (display_name);
976           g_free (filename_copy);
977           return handle;
978         }
979
980       folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
981       folder_unix->system_unix = system_unix;
982       folder_unix->filename = filename_copy;
983       folder_unix->types = types;
984       folder_unix->stat_info = NULL;
985       folder_unix->load_folder_id = 0;
986       folder_unix->have_mime_type = FALSE;
987       folder_unix->have_stat = FALSE;
988       folder_unix->have_hidden = FALSE;
989       folder_unix->is_finished_loading = FALSE;
990       set_asof = TRUE;
991           
992       if ((system_unix->have_afs &&
993            system_unix->afs_statbuf.st_dev == statbuf.st_dev &&
994            system_unix->afs_statbuf.st_ino == statbuf.st_ino) ||
995           (system_unix->have_net &&
996            system_unix->net_statbuf.st_dev == statbuf.st_dev &&
997            system_unix->net_statbuf.st_ino == statbuf.st_ino))
998         folder_unix->is_network_dir = TRUE;
999       else
1000         folder_unix->is_network_dir = FALSE;
1001
1002       g_hash_table_insert (system_unix->folder_hash,
1003                            folder_unix->filename,
1004                            folder_unix);
1005     }
1006
1007   if (set_asof)
1008     folder_unix->asof = time (NULL);
1009
1010   g_object_ref (handle);
1011   queue_get_folder_callback (callback, handle, GTK_FILE_FOLDER (folder_unix), NULL, data);
1012
1013   /* Start loading the folder contents in an idle */
1014   if (!folder_unix->load_folder_id)
1015     folder_unix->load_folder_id =
1016       g_idle_add ((GSourceFunc) load_folder, folder_unix);
1017
1018   return handle;
1019 }
1020
1021 static GtkFileSystemHandle *
1022 gtk_file_system_unix_create_folder (GtkFileSystem                     *file_system,
1023                                     const GtkFilePath                 *path,
1024                                     GtkFileSystemCreateFolderCallback  callback,
1025                                     gpointer                           data)
1026 {
1027   GError *error = NULL;
1028   GtkFileSystemUnix *system_unix;
1029   GtkFileSystemHandle *handle;
1030   const char *filename;
1031   gboolean result;
1032   char *parent, *tmp;
1033   int save_errno = errno;
1034
1035   system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
1036
1037   filename = gtk_file_path_get_string (path);
1038   g_return_val_if_fail (filename != NULL, FALSE);
1039   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
1040
1041   handle = create_handle (file_system);
1042
1043   tmp = remove_trailing_slash (filename);
1044   errno = 0;
1045   result = mkdir (tmp, 0777) == 0;
1046   save_errno = errno;
1047   g_free (tmp);
1048
1049   if (!result)
1050     {
1051       gchar *display_name = g_filename_display_name (filename);
1052       g_set_error (&error,
1053                    GTK_FILE_SYSTEM_ERROR,
1054                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1055                    _("Error creating directory '%s': %s"),
1056                    display_name,
1057                    g_strerror (save_errno));
1058       
1059       g_object_ref (handle);
1060       queue_create_folder_callback (callback, handle, path, error, data);
1061
1062       g_free (display_name);
1063       return handle;
1064     }
1065
1066   g_object_ref (handle);
1067   queue_create_folder_callback (callback, handle, path, NULL, data);
1068
1069   parent = get_parent_dir (filename);
1070   if (parent)
1071     {
1072       GtkFileFolderUnix *folder_unix;
1073
1074       folder_unix = g_hash_table_lookup (system_unix->folder_hash, parent);
1075       if (folder_unix)
1076         {
1077           GSList *paths;
1078           char *basename;
1079           struct stat_info_entry *entry;
1080
1081           /* Make sure the new folder exists in the parent's folder */
1082           entry = g_new0 (struct stat_info_entry, 1);
1083           if (folder_unix->is_network_dir)
1084             {
1085               entry->statbuf.st_mode = S_IFDIR;
1086               entry->mime_type = g_strdup ("x-directory/normal");
1087             }
1088
1089           basename = g_path_get_basename (filename);
1090           g_hash_table_insert (folder_unix->stat_info,
1091                                basename,
1092                                entry);
1093
1094           if (folder_unix->have_stat)
1095             {
1096               /* Cheating */
1097               if ((folder_unix->types & STAT_NEEDED_MASK) != 0)
1098                 cb_fill_in_stats (basename, entry, folder_unix);
1099
1100               if ((folder_unix->types & GTK_FILE_INFO_MIME_TYPE) != 0)
1101                 cb_fill_in_mime_type (basename, entry, folder_unix);
1102             }
1103
1104           paths = g_slist_append (NULL, (GtkFilePath *) path);
1105           g_signal_emit_by_name (folder_unix, "files-added", paths);
1106           g_slist_free (paths);
1107         }
1108
1109       g_free (parent);
1110     }
1111
1112   return handle;
1113 }
1114
1115 static void
1116 gtk_file_system_unix_cancel_operation (GtkFileSystemHandle *handle)
1117 {
1118   /* We don't set "cancelled" to TRUE here, since the actual operation
1119    * is executed in the function itself and not in a callback.  So
1120    * the operations can never be cancelled (since they will be already
1121    * completed at this point.
1122    */
1123 }
1124
1125 static void
1126 gtk_file_system_unix_volume_free (GtkFileSystem        *file_system,
1127                                   GtkFileSystemVolume  *volume)
1128 {
1129   GtkFilePath *path;
1130
1131   path = (GtkFilePath *) volume;
1132   gtk_file_path_free (path);
1133 }
1134
1135 static GtkFilePath *
1136 gtk_file_system_unix_volume_get_base_path (GtkFileSystem        *file_system,
1137                                            GtkFileSystemVolume  *volume)
1138 {
1139   return gtk_file_path_new_dup ("/");
1140 }
1141
1142 static gboolean
1143 gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem        *file_system,
1144                                             GtkFileSystemVolume  *volume)
1145 {
1146   return TRUE;
1147 }
1148
1149 static GtkFileSystemHandle *
1150 gtk_file_system_unix_volume_mount (GtkFileSystem                    *file_system,
1151                                    GtkFileSystemVolume              *volume,
1152                                    GtkFileSystemVolumeMountCallback  callback,
1153                                    gpointer                          data)
1154 {
1155   GError *error = NULL;
1156   GtkFileSystemHandle *handle = create_handle (file_system);
1157
1158   g_set_error (&error,
1159                GTK_FILE_SYSTEM_ERROR,
1160                GTK_FILE_SYSTEM_ERROR_FAILED,
1161                _("This file system does not support mounting"));
1162
1163   g_object_ref (handle);
1164   queue_volume_mount_callback (callback, handle, volume, error, data);
1165
1166   return handle;
1167 }
1168
1169 static gchar *
1170 gtk_file_system_unix_volume_get_display_name (GtkFileSystem       *file_system,
1171                                               GtkFileSystemVolume *volume)
1172 {
1173   return g_strdup (_("File System")); /* Same as Nautilus */
1174 }
1175
1176 static IconType
1177 get_icon_type_from_stat (struct stat *statp)
1178 {
1179   if (S_ISBLK (statp->st_mode))
1180     return ICON_BLOCK_DEVICE;
1181   else if (S_ISLNK (statp->st_mode))
1182     return ICON_BROKEN_SYMBOLIC_LINK; /* See get_icon_type */
1183   else if (S_ISCHR (statp->st_mode))
1184     return ICON_CHARACTER_DEVICE;
1185   else if (S_ISDIR (statp->st_mode))
1186     return ICON_DIRECTORY;
1187 #ifdef S_ISFIFO
1188   else if (S_ISFIFO (statp->st_mode))
1189     return  ICON_FIFO;
1190 #endif
1191 #ifdef S_ISSOCK
1192   else if (S_ISSOCK (statp->st_mode))
1193     return ICON_SOCKET;
1194 #endif
1195   else
1196     return ICON_REGULAR;
1197 }
1198
1199 static IconType
1200 get_icon_type (const char *filename,
1201                GError    **error)
1202 {
1203   struct stat statbuf;
1204
1205   /* If stat fails, try to fall back to lstat to catch broken links
1206    */
1207   if (stat (filename, &statbuf) != 0)
1208     {
1209       if (errno != ENOENT || lstat (filename, &statbuf) != 0)
1210         {
1211           int save_errno = errno;
1212           gchar *display_name = g_filename_display_name (filename);
1213           g_set_error (error,
1214                        GTK_FILE_SYSTEM_ERROR,
1215                        GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1216                        _("Error getting information for '%s': %s"),
1217                        display_name,
1218                        g_strerror (save_errno));
1219           g_free (display_name);
1220
1221           return ICON_NONE;
1222         }
1223     }
1224
1225   return get_icon_type_from_stat (&statbuf);
1226 }
1227
1228 /* Renders a fallback icon from the stock system */
1229 static const gchar *
1230 get_fallback_icon_name (IconType icon_type)
1231 {
1232   const char *stock_name;
1233
1234   switch (icon_type)
1235     {
1236     case ICON_BLOCK_DEVICE:
1237       stock_name = GTK_STOCK_HARDDISK;
1238       break;
1239
1240     case ICON_DIRECTORY:
1241       stock_name = GTK_STOCK_DIRECTORY;
1242       break;
1243
1244     case ICON_EXECUTABLE:
1245       stock_name = GTK_STOCK_EXECUTE;
1246       break;
1247
1248     default:
1249       stock_name = GTK_STOCK_FILE;
1250       break;
1251     }
1252
1253   return stock_name;
1254 }
1255
1256 static gchar *
1257 gtk_file_system_unix_volume_get_icon_name (GtkFileSystem        *file_system,
1258                                            GtkFileSystemVolume  *volume,
1259                                            GError              **error)
1260 {
1261   /* FIXME: maybe we just always want to return GTK_STOCK_HARDDISK here?
1262    * or the new tango icon name?
1263    */
1264   return g_strdup ("gnome-dev-harddisk");
1265 }
1266
1267 static char *
1268 get_parent_dir (const char *filename)
1269 {
1270   int len;
1271
1272   len = strlen (filename);
1273
1274   /* Ignore trailing slashes */
1275   if (len > 1 && filename[len - 1] == '/')
1276     {
1277       char *tmp, *parent;
1278
1279       tmp = g_strndup (filename, len - 1);
1280
1281       parent = g_path_get_dirname (tmp);
1282       g_free (tmp);
1283
1284       return parent;
1285     }
1286   else
1287     return g_path_get_dirname (filename);
1288 }
1289
1290 static gboolean
1291 gtk_file_system_unix_get_parent (GtkFileSystem     *file_system,
1292                                  const GtkFilePath *path,
1293                                  GtkFilePath      **parent,
1294                                  GError           **error)
1295 {
1296   const char *filename;
1297
1298   filename = gtk_file_path_get_string (path);
1299   g_return_val_if_fail (filename != NULL, FALSE);
1300   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
1301
1302   if (filename_is_root (filename))
1303     {
1304       *parent = NULL;
1305     }
1306   else
1307     {
1308       gchar *parent_filename = get_parent_dir (filename);
1309       *parent = filename_to_path (parent_filename);
1310       g_free (parent_filename);
1311     }
1312
1313   return TRUE;
1314 }
1315
1316 static GtkFilePath *
1317 gtk_file_system_unix_make_path (GtkFileSystem    *file_system,
1318                                const GtkFilePath *base_path,
1319                                const gchar       *display_name,
1320                                GError           **error)
1321 {
1322   const char *base_filename;
1323   gchar *filename;
1324   gchar *full_filename;
1325   GError *tmp_error = NULL;
1326   GtkFilePath *result;
1327
1328   base_filename = gtk_file_path_get_string (base_path);
1329   g_return_val_if_fail (base_filename != NULL, NULL);
1330   g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
1331
1332   if (strchr (display_name, G_DIR_SEPARATOR))
1333     {
1334       g_set_error (error,
1335                    GTK_FILE_SYSTEM_ERROR,
1336                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1337                    _("The name \"%s\" is not valid because it contains the character \"%s\". "
1338                      "Please use a different name."),
1339                    display_name,
1340                    G_DIR_SEPARATOR_S);
1341       return NULL;
1342     }
1343
1344   filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
1345   if (!filename)
1346     {
1347       g_set_error (error,
1348                    GTK_FILE_SYSTEM_ERROR,
1349                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1350                    "%s",
1351                    tmp_error->message);
1352
1353       g_error_free (tmp_error);
1354
1355       return NULL;
1356     }
1357
1358   full_filename = g_build_filename (base_filename, filename, NULL);
1359   result = filename_to_path (full_filename);
1360   g_free (filename);
1361   g_free (full_filename);
1362
1363   return result;
1364 }
1365
1366 /* If this was a publically exported function, it should return
1367  * a dup'ed result, but we make it modify-in-place for efficiency
1368  * here, and because it works for us.
1369  */
1370 static void
1371 canonicalize_filename (gchar *filename)
1372 {
1373   gchar *p, *q;
1374   gboolean last_was_slash = FALSE;
1375
1376   p = filename;
1377   q = filename;
1378
1379   while (*p)
1380     {
1381       if (*p == G_DIR_SEPARATOR)
1382         {
1383           if (!last_was_slash)
1384             *q++ = G_DIR_SEPARATOR;
1385
1386           last_was_slash = TRUE;
1387         }
1388       else
1389         {
1390           if (last_was_slash && *p == '.')
1391             {
1392               if (*(p + 1) == G_DIR_SEPARATOR ||
1393                   *(p + 1) == '\0')
1394                 {
1395                   if (*(p + 1) == '\0')
1396                     break;
1397
1398                   p += 1;
1399                 }
1400               else if (*(p + 1) == '.' &&
1401                        (*(p + 2) == G_DIR_SEPARATOR ||
1402                         *(p + 2) == '\0'))
1403                 {
1404                   if (q > filename + 1)
1405                     {
1406                       q--;
1407                       while (q > filename + 1 &&
1408                              *(q - 1) != G_DIR_SEPARATOR)
1409                         q--;
1410                     }
1411
1412                   if (*(p + 2) == '\0')
1413                     break;
1414
1415                   p += 2;
1416                 }
1417               else
1418                 {
1419                   *q++ = *p;
1420                   last_was_slash = FALSE;
1421                 }
1422             }
1423           else
1424             {
1425               *q++ = *p;
1426               last_was_slash = FALSE;
1427             }
1428         }
1429
1430       p++;
1431     }
1432
1433   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
1434     q--;
1435
1436   *q = '\0';
1437 }
1438
1439 /* Takes a user-typed filename and expands a tilde at the beginning of the string */
1440 static char *
1441 expand_tilde (const char *filename)
1442 {
1443   const char *notilde;
1444   const char *slash;
1445   const char *home;
1446
1447   if (filename[0] != '~')
1448     return g_strdup (filename);
1449
1450   notilde = filename + 1;
1451
1452   slash = strchr (notilde, G_DIR_SEPARATOR);
1453   if (!slash)
1454     return NULL;
1455
1456   if (slash == notilde)
1457     {
1458       home = g_get_home_dir ();
1459
1460       if (!home)
1461         return g_strdup (filename);
1462     }
1463   else
1464     {
1465       char *username;
1466       struct passwd *passwd;
1467
1468       username = g_strndup (notilde, slash - notilde);
1469       passwd = getpwnam (username);
1470       g_free (username);
1471
1472       if (!passwd)
1473         return g_strdup (filename);
1474
1475       home = passwd->pw_dir;
1476     }
1477
1478   return g_build_filename (home, G_DIR_SEPARATOR_S, slash + 1, NULL);
1479 }
1480
1481 static gboolean
1482 gtk_file_system_unix_parse (GtkFileSystem     *file_system,
1483                             const GtkFilePath *base_path,
1484                             const gchar       *str,
1485                             GtkFilePath      **folder,
1486                             gchar            **file_part,
1487                             GError           **error)
1488 {
1489   const char *base_filename;
1490   gchar *filename;
1491   gchar *last_slash;
1492   gboolean result = FALSE;
1493
1494   base_filename = gtk_file_path_get_string (base_path);
1495   g_return_val_if_fail (base_filename != NULL, FALSE);
1496   g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
1497
1498   filename = expand_tilde (str);
1499   if (!filename)
1500     {
1501       g_set_error (error,
1502                    GTK_FILE_SYSTEM_ERROR,
1503                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1504                    "%s", ""); /* nothing for now, as we are string-frozen */
1505       return FALSE;
1506     }
1507
1508   last_slash = strrchr (filename, G_DIR_SEPARATOR);
1509   if (!last_slash)
1510     {
1511       *folder = gtk_file_path_copy (base_path);
1512       *file_part = g_strdup (filename);
1513       result = TRUE;
1514     }
1515   else
1516     {
1517       gchar *folder_part;
1518       gchar *folder_path;
1519       GError *tmp_error = NULL;
1520
1521       if (last_slash == filename)
1522         folder_part = g_strdup ("/");
1523       else
1524         folder_part = g_filename_from_utf8 (filename, last_slash - filename,
1525                                             NULL, NULL, &tmp_error);
1526
1527       if (!folder_part)
1528         {
1529           g_set_error (error,
1530                        GTK_FILE_SYSTEM_ERROR,
1531                        GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1532                        "%s",
1533                        tmp_error->message);
1534           g_error_free (tmp_error);
1535         }
1536       else
1537         {
1538           if (folder_part[0] == G_DIR_SEPARATOR)
1539             folder_path = folder_part;
1540           else
1541             {
1542               folder_path = g_build_filename (base_filename, folder_part, NULL);
1543               g_free (folder_part);
1544             }
1545
1546           canonicalize_filename (folder_path);
1547
1548           *folder = filename_to_path (folder_path);
1549           *file_part = g_strdup (last_slash + 1);
1550
1551           g_free (folder_path);
1552
1553           result = TRUE;
1554         }
1555     }
1556
1557   g_free (filename);
1558
1559   return result;
1560 }
1561
1562 static gchar *
1563 gtk_file_system_unix_path_to_uri (GtkFileSystem     *file_system,
1564                                   const GtkFilePath *path)
1565 {
1566   return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
1567 }
1568
1569 static gchar *
1570 gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
1571                                        const GtkFilePath *path)
1572 {
1573   return g_strdup (gtk_file_path_get_string (path));
1574 }
1575
1576 static GtkFilePath *
1577 gtk_file_system_unix_uri_to_path (GtkFileSystem     *file_system,
1578                                   const gchar       *uri)
1579 {
1580   GtkFilePath *path;
1581   gchar *filename = g_filename_from_uri (uri, NULL, NULL);
1582
1583   if (filename)
1584     {
1585       path = filename_to_path (filename);
1586       g_free (filename);
1587     }
1588   else
1589     path = NULL;
1590
1591   return path;
1592 }
1593
1594 static GtkFilePath *
1595 gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
1596                                        const gchar   *filename)
1597 {
1598   return filename_to_path (filename);
1599 }
1600
1601 /* Returns the name of the icon to be used for a path which is known to be a
1602  * directory.  This can vary for Home, Desktop, etc.
1603  */
1604 static const char *
1605 get_icon_name_for_directory (const char *path)
1606 {
1607   static char *desktop_path = NULL;
1608
1609   if (!g_get_home_dir ())
1610     return "gnome-fs-directory";
1611
1612   if (!desktop_path)
1613       desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1614
1615   if (strcmp (g_get_home_dir (), path) == 0)
1616     return "gnome-fs-home";
1617   else if (strcmp (desktop_path, path) == 0)
1618     return "gnome-fs-desktop";
1619   else
1620     return "gnome-fs-directory";
1621
1622   return NULL;
1623 }
1624
1625 /* Computes our internal icon type based on a path name; also returns the MIME
1626  * type in case we come up with ICON_REGULAR.
1627  */
1628 static IconType
1629 get_icon_type_from_path (GtkFileFolderUnix *folder_unix,
1630                          struct stat       *statbuf,
1631                          const char        *filename,
1632                          const char       **mime_type)
1633 {
1634   IconType icon_type;
1635
1636   *mime_type = NULL;
1637
1638   if (folder_unix && folder_unix->have_stat)
1639     {
1640       char *basename;
1641       struct stat_info_entry *entry;
1642
1643       g_assert (folder_unix->stat_info != NULL);
1644
1645       basename = g_path_get_basename (filename);
1646       entry = g_hash_table_lookup (folder_unix->stat_info, basename);
1647       g_free (basename);
1648       if (entry)
1649         {
1650           if (entry->icon_type == ICON_UNDECIDED)
1651             {
1652               entry->icon_type = get_icon_type_from_stat (&entry->statbuf);
1653               g_assert (entry->icon_type != ICON_UNDECIDED);
1654             }
1655           icon_type = entry->icon_type;
1656           if (icon_type == ICON_REGULAR)
1657             {
1658               fill_in_mime_type (folder_unix);
1659               *mime_type = entry->mime_type;
1660             }
1661
1662           return icon_type;
1663         }
1664     }
1665
1666   if (statbuf)
1667     return get_icon_type_from_stat (statbuf);
1668
1669   icon_type = get_icon_type (filename, NULL);
1670   if (icon_type == ICON_REGULAR)
1671     *mime_type = xdg_mime_get_mime_type_for_file (filename, NULL);
1672
1673   return icon_type;
1674 }
1675
1676 /* Renders an icon for a non-ICON_REGULAR file */
1677 static const gchar *
1678 get_special_icon_name (IconType           icon_type,
1679                        const gchar       *filename)
1680 {
1681   const char *name;
1682
1683   g_assert (icon_type != ICON_REGULAR);
1684
1685   switch (icon_type)
1686     {
1687     case ICON_BLOCK_DEVICE:
1688       name = "gnome-fs-blockdev";
1689       break;
1690     case ICON_BROKEN_SYMBOLIC_LINK:
1691       name = "gnome-fs-symlink";
1692       break;
1693     case ICON_CHARACTER_DEVICE:
1694       name = "gnome-fs-chardev";
1695       break;
1696     case ICON_DIRECTORY:
1697       /* get_icon_name_for_directory() returns a dupped string */
1698       return get_icon_name_for_directory (filename);
1699     case ICON_EXECUTABLE:
1700       name ="gnome-fs-executable";
1701       break;
1702     case ICON_FIFO:
1703       name = "gnome-fs-fifo";
1704       break;
1705     case ICON_SOCKET:
1706       name = "gnome-fs-socket";
1707       break;
1708     default:
1709       g_assert_not_reached ();
1710       return NULL;
1711     }
1712
1713   return name;
1714 }
1715
1716 static gchar *
1717 get_icon_name_for_mime_type (const char *mime_type)
1718 {
1719   char *name;
1720   const char *separator;
1721   GString *icon_name;
1722
1723   if (!mime_type)
1724     return NULL;
1725
1726   separator = strchr (mime_type, '/');
1727   if (!separator)
1728     return NULL; /* maybe we should return a GError with "invalid MIME-type" */
1729
1730   /* FIXME: we default to the gnome icon naming for now.  Some question
1731    * as below, how are we going to handle a second attempt?
1732    */
1733 #if 0
1734   icon_name = g_string_new ("");
1735   g_string_append_len (icon_name, mime_type, separator - mime_type);
1736   g_string_append_c (icon_name, '-');
1737   g_string_append (icon_name, separator + 1);
1738   pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1739   g_string_free (icon_name, TRUE);
1740   if (pixbuf)
1741     return pixbuf;
1742
1743   icon_name = g_string_new ("");
1744   g_string_append_len (icon_name, mime_type, separator - mime_type);
1745   g_string_append (icon_name, "-x-generic");
1746   pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1747   g_string_free (icon_name, TRUE);
1748   if (pixbuf)
1749     return pixbuf;
1750 #endif
1751
1752   icon_name = g_string_new ("gnome-mime-");
1753   g_string_append_len (icon_name, mime_type, separator - mime_type);
1754   g_string_append_c (icon_name, '-');
1755   g_string_append (icon_name, separator + 1);
1756   name = icon_name->str;
1757   g_string_free (icon_name, FALSE);
1758
1759   return name;
1760
1761   /* FIXME: how are we going to implement a second attempt? */
1762 #if 0
1763   if (pixbuf)
1764     return pixbuf;
1765
1766   icon_name = g_string_new ("gnome-mime-");
1767   g_string_append_len (icon_name, mime_type, separator - mime_type);
1768   pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1769   g_string_free (icon_name, TRUE);
1770
1771   return pixbuf;
1772 #endif
1773 }
1774
1775 static void
1776 bookmark_list_free (GSList *list)
1777 {
1778   GSList *l;
1779
1780   for (l = list; l; l = l->next)
1781     g_free (l->data);
1782
1783   g_slist_free (list);
1784 }
1785
1786 /* Returns whether a URI is a local file:// */
1787 static gboolean
1788 is_local_uri (const char *uri)
1789 {
1790   char *filename;
1791   char *hostname;
1792   gboolean result;
1793
1794   /* This is rather crude, but hey */
1795   filename = g_filename_from_uri (uri, &hostname, NULL);
1796
1797   result = (filename && !hostname);
1798
1799   g_free (filename);
1800   g_free (hostname);
1801
1802   return result;
1803 }
1804
1805 static char *
1806 bookmark_get_filename (void)
1807 {
1808   char *filename;
1809
1810   filename = g_build_filename (g_get_home_dir (), 
1811                                BOOKMARKS_FILENAME, NULL);
1812   g_assert (filename != NULL);
1813   return filename;
1814 }
1815
1816 static gboolean
1817 bookmark_list_read (GSList **bookmarks, GError **error)
1818 {
1819   gchar *filename;
1820   gchar *contents;
1821   gboolean result = FALSE;
1822
1823   filename = bookmark_get_filename ();
1824   *bookmarks = NULL;
1825
1826   if (g_file_get_contents (filename, &contents, NULL, error))
1827     {
1828       gchar **lines = g_strsplit (contents, "\n", -1);
1829       int i;
1830       GHashTable *table;
1831
1832       table = g_hash_table_new (g_str_hash, g_str_equal);
1833
1834       for (i = 0; lines[i]; i++)
1835         {
1836           if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
1837             {
1838               *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
1839               g_hash_table_insert (table, lines[i], lines[i]);
1840             }
1841         }
1842
1843       g_free (contents);
1844       g_hash_table_destroy (table);
1845       g_strfreev (lines);
1846
1847       *bookmarks = g_slist_reverse (*bookmarks);
1848       result = TRUE;
1849     }
1850
1851   g_free (filename);
1852
1853   return result;
1854 }
1855
1856 static gboolean
1857 bookmark_list_write (GSList  *bookmarks, 
1858                      GError **error)
1859 {
1860   GSList *l;
1861   GString *string;
1862   char *filename;
1863   GError *tmp_error = NULL;
1864   gboolean result;
1865
1866   string = g_string_new ("");
1867
1868   for (l = bookmarks; l; l = l->next)
1869     {
1870       g_string_append (string, l->data);
1871       g_string_append_c (string, '\n');
1872     }
1873
1874   filename = bookmark_get_filename ();
1875
1876   result = g_file_set_contents (filename, string->str, -1, &tmp_error);
1877
1878   g_free (filename);
1879   g_string_free (string, TRUE);
1880
1881   if (!result)
1882     {
1883       g_set_error (error,
1884                    GTK_FILE_SYSTEM_ERROR,
1885                    GTK_FILE_SYSTEM_ERROR_FAILED,
1886                    _("Bookmark saving failed: %s"),
1887                    tmp_error->message);
1888       
1889       g_error_free (tmp_error);
1890     }
1891
1892   return result;
1893 }
1894
1895 static gboolean
1896 gtk_file_system_unix_insert_bookmark (GtkFileSystem     *file_system,
1897                                       const GtkFilePath *path,
1898                                       gint               position,
1899                                       GError           **error)
1900 {
1901   GSList *bookmarks;
1902   int num_bookmarks;
1903   GSList *l;
1904   char *uri;
1905   gboolean result;
1906   GError *err;
1907
1908   err = NULL;
1909   if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
1910     {
1911       g_propagate_error (error, err);
1912       return FALSE;
1913     }
1914
1915   num_bookmarks = g_slist_length (bookmarks);
1916   g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
1917
1918   result = FALSE;
1919
1920   uri = gtk_file_system_unix_path_to_uri (file_system, path);
1921
1922   for (l = bookmarks; l; l = l->next)
1923     {
1924       char *bookmark, *space;
1925
1926       bookmark = l->data;
1927       
1928       space = strchr (bookmark, ' ');
1929       if (space)
1930         *space = '\0';
1931       if (strcmp (bookmark, uri) != 0)
1932         {
1933           if (space)
1934             *space = ' ';
1935         }
1936       else
1937         {
1938           g_set_error (error,
1939                        GTK_FILE_SYSTEM_ERROR,
1940                        GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
1941                        _("'%s' already exists in the bookmarks list"),
1942                        uri);
1943           goto out;
1944         }
1945     }
1946
1947   bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
1948   if (bookmark_list_write (bookmarks, error))
1949     {
1950       result = TRUE;
1951       g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1952     }
1953
1954  out:
1955
1956   g_free (uri);
1957   bookmark_list_free (bookmarks);
1958
1959   return result;
1960 }
1961
1962 static gboolean
1963 gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
1964                                       const GtkFilePath *path,
1965                                       GError           **error)
1966 {
1967   GSList *bookmarks;
1968   char *uri;
1969   GSList *l;
1970   gboolean result;
1971
1972   if (!bookmark_list_read (&bookmarks, error))
1973     return FALSE;
1974
1975   result = FALSE;
1976
1977   uri = gtk_file_system_path_to_uri (file_system, path);
1978
1979   for (l = bookmarks; l; l = l->next)
1980     {
1981       char *bookmark, *space;
1982
1983       bookmark = (char *)l->data;
1984       space = strchr (bookmark, ' ');
1985       if (space)
1986         *space = '\0';
1987
1988       if (strcmp (bookmark, uri) != 0)
1989         {
1990           if (space)
1991             *space = ' ';
1992         }
1993       else
1994         {
1995           g_free (l->data);
1996           bookmarks = g_slist_remove_link (bookmarks, l);
1997           g_slist_free_1 (l);
1998
1999           if (bookmark_list_write (bookmarks, error))
2000             {
2001               result = TRUE;
2002
2003               g_signal_emit_by_name (file_system, "bookmarks-changed", 0);            
2004             }
2005
2006           goto out;
2007         }
2008     }
2009
2010   g_set_error (error,
2011                GTK_FILE_SYSTEM_ERROR,
2012                GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
2013                _("'%s' does not exist in the bookmarks list"),
2014                uri);
2015
2016  out:
2017
2018   g_free (uri);
2019   bookmark_list_free (bookmarks);
2020
2021   return result;
2022 }
2023
2024 static GSList *
2025 gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
2026 {
2027   GSList *bookmarks;
2028   GSList *result;
2029   GSList *l;
2030
2031   if (!bookmark_list_read (&bookmarks, NULL))
2032     return NULL;
2033
2034   result = NULL;
2035
2036   for (l = bookmarks; l; l = l->next)
2037     {
2038       char *bookmark, *space;
2039
2040       bookmark = (char *)l->data;
2041       space = strchr (bookmark, ' ');
2042       if (space)
2043         *space = '\0';
2044
2045       if (is_local_uri (bookmark))
2046         result = g_slist_prepend (result, gtk_file_system_unix_uri_to_path (file_system, bookmark));
2047     }
2048
2049   bookmark_list_free (bookmarks);
2050
2051   result = g_slist_reverse (result);
2052   return result;
2053 }
2054
2055 static gchar *
2056 gtk_file_system_unix_get_bookmark_label (GtkFileSystem     *file_system,
2057                                          const GtkFilePath *path)
2058 {
2059   GSList *labels;
2060   gchar *label;
2061   GSList *l;
2062   char *bookmark, *space, *uri;
2063   
2064   labels = NULL;
2065   label = NULL;
2066
2067   uri = gtk_file_system_path_to_uri (file_system, path);
2068   bookmark_list_read (&labels, NULL);
2069
2070   for (l = labels; l && !label; l = l->next) 
2071     {
2072       bookmark = (char *)l->data;
2073       space = strchr (bookmark, ' ');
2074       if (!space)
2075         continue;
2076
2077       *space = '\0';
2078
2079       if (strcmp (uri, bookmark) == 0)
2080         label = g_strdup (space + 1);
2081     }
2082
2083   bookmark_list_free (labels);
2084   g_free (uri);
2085
2086   return label;
2087 }
2088
2089 static void
2090 gtk_file_system_unix_set_bookmark_label (GtkFileSystem     *file_system,
2091                                          const GtkFilePath *path,
2092                                          const gchar       *label)
2093 {
2094   GSList *labels;
2095   GSList *l;
2096   gchar *bookmark, *space, *uri;
2097   gboolean found;
2098
2099   labels = NULL;
2100
2101   uri = gtk_file_system_path_to_uri (file_system, path);
2102   bookmark_list_read (&labels, NULL);
2103
2104   found = FALSE;
2105   for (l = labels; l && !found; l = l->next) 
2106     {
2107       bookmark = (gchar *)l->data;
2108       space = strchr (bookmark, ' ');
2109       if (space)
2110         *space = '\0';
2111
2112       if (strcmp (bookmark, uri) != 0)
2113         {
2114           if (space)
2115             *space = ' ';
2116         }
2117       else
2118         {
2119           g_free (bookmark);
2120           
2121           if (label && *label)
2122             l->data = g_strdup_printf ("%s %s", uri, label);
2123           else
2124             l->data = g_strdup (uri);
2125
2126           found = TRUE;
2127           break;
2128         }
2129     }
2130
2131   if (found)
2132     {
2133       if (bookmark_list_write (labels, NULL))
2134         g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
2135     }
2136   
2137   bookmark_list_free (labels);
2138   g_free (uri);
2139 }
2140
2141 static void
2142 _gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
2143 {
2144   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
2145
2146   gobject_class->finalize = gtk_file_folder_unix_finalize;
2147 }
2148
2149 static void
2150 gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
2151 {
2152   iface->get_info = gtk_file_folder_unix_get_info;
2153   iface->list_children = gtk_file_folder_unix_list_children;
2154   iface->is_finished_loading = gtk_file_folder_unix_is_finished_loading;
2155 }
2156
2157 static void
2158 _gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
2159 {
2160 }
2161
2162 static void
2163 gtk_file_folder_unix_finalize (GObject *object)
2164 {
2165   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
2166
2167   if (folder_unix->load_folder_id)
2168     {
2169       g_source_remove (folder_unix->load_folder_id);
2170       folder_unix->load_folder_id = 0;
2171     }
2172
2173   g_hash_table_remove (folder_unix->system_unix->folder_hash, folder_unix->filename);
2174
2175   if (folder_unix->stat_info)
2176     {
2177 #if 0
2178       g_print ("Releasing information for directory %s\n", folder_unix->filename);
2179 #endif
2180       g_hash_table_destroy (folder_unix->stat_info);
2181     }
2182
2183   g_free (folder_unix->filename);
2184
2185   G_OBJECT_CLASS (_gtk_file_folder_unix_parent_class)->finalize (object);
2186 }
2187
2188 /* Creates a GtkFileInfo for "/" by stat()ing it */
2189 static GtkFileInfo *
2190 file_info_for_root_with_error (const char  *root_name,
2191                                GError     **error)
2192 {
2193   struct stat statbuf;
2194   GtkFileInfo *info;
2195
2196   if (stat (root_name, &statbuf) != 0)
2197     {
2198       int saved_errno;
2199
2200       saved_errno = errno; 
2201       g_set_error (error,
2202                    GTK_FILE_SYSTEM_ERROR,
2203                    GTK_FILE_SYSTEM_ERROR_FAILED,
2204                    _("Error getting information for '%s': %s"),
2205                    "/", g_strerror (saved_errno));
2206
2207       return NULL;
2208     }
2209
2210   info = gtk_file_info_new ();
2211   gtk_file_info_set_display_name (info, "/");
2212   gtk_file_info_set_is_folder (info, TRUE);
2213   gtk_file_info_set_is_hidden (info, FALSE);
2214   gtk_file_info_set_mime_type (info, "x-directory/normal");
2215   gtk_file_info_set_modification_time (info, statbuf.st_mtime);
2216   gtk_file_info_set_size (info, statbuf.st_size);
2217
2218   return info;
2219 }
2220
2221 static gboolean
2222 stat_with_error (const char   *filename,
2223                  struct stat  *statbuf,
2224                  GError      **error)
2225 {
2226   if (stat (filename, statbuf) == -1 &&
2227       (errno != ENOENT || lstat (filename, statbuf) == -1))
2228     {
2229       int saved_errno;
2230       int code;
2231       char *display_name;
2232
2233       saved_errno = errno;
2234
2235       if (saved_errno == ENOENT)
2236         code = GTK_FILE_SYSTEM_ERROR_NONEXISTENT;
2237       else
2238         code = GTK_FILE_SYSTEM_ERROR_FAILED;
2239
2240       display_name = g_filename_display_name (filename);
2241       g_set_error (error,
2242                    GTK_FILE_SYSTEM_ERROR,
2243                    code,
2244                    _("Error getting information for '%s': %s"),
2245                    display_name,
2246                    g_strerror (saved_errno));
2247
2248       g_free (display_name);
2249       return FALSE;
2250     }
2251
2252   return TRUE;
2253 }
2254
2255 /* Creates a new GtkFileInfo from the specified data */
2256 static GtkFileInfo *
2257 create_file_info (GtkFileFolderUnix *folder_unix,
2258                   const char        *filename,
2259                   const char        *basename,
2260                   GtkFileInfoType    types,
2261                   struct stat       *statbuf,
2262                   const char        *mime_type)
2263 {
2264   GtkFileInfo *info;
2265
2266   info = gtk_file_info_new ();
2267   
2268   if (types & GTK_FILE_INFO_DISPLAY_NAME)
2269     {
2270       gchar *display_name = g_filename_display_basename (filename);
2271       gtk_file_info_set_display_name (info, display_name);
2272       g_free (display_name);
2273     }
2274
2275   if (types & GTK_FILE_INFO_IS_HIDDEN)
2276     {
2277       if (folder_unix)
2278         {
2279           if (file_is_hidden (folder_unix, basename))
2280             gtk_file_info_set_is_hidden (info, TRUE);
2281         }
2282       else
2283         {
2284           if (get_is_hidden_for_file (filename, basename))
2285             gtk_file_info_set_is_hidden (info, TRUE);
2286         }
2287     }
2288
2289   if (types & GTK_FILE_INFO_IS_FOLDER)
2290     gtk_file_info_set_is_folder (info, S_ISDIR (statbuf->st_mode));
2291
2292   if (types & GTK_FILE_INFO_MIME_TYPE)
2293     gtk_file_info_set_mime_type (info, mime_type);
2294
2295   if (types & GTK_FILE_INFO_MODIFICATION_TIME)
2296     gtk_file_info_set_modification_time (info, statbuf->st_mtime);
2297
2298   if (types & GTK_FILE_INFO_SIZE)
2299     gtk_file_info_set_size (info, (gint64) statbuf->st_size);
2300
2301   if (types & GTK_FILE_INFO_ICON)
2302     {
2303       IconType icon_type;
2304       gboolean free_icon_name = FALSE;
2305       const char *icon_name;
2306       const char *icon_mime_type;
2307
2308       icon_type = get_icon_type_from_path (folder_unix, statbuf, filename, &icon_mime_type);
2309
2310       switch (icon_type)
2311         {
2312           case ICON_NONE:
2313             icon_name = get_fallback_icon_name (icon_type);
2314             break;
2315
2316           case ICON_REGULAR:
2317             free_icon_name = TRUE;
2318             if (icon_mime_type)
2319               icon_name = get_icon_name_for_mime_type (icon_mime_type);
2320             else
2321               icon_name = get_icon_name_for_mime_type (mime_type);
2322             break;
2323
2324           default:
2325             icon_name = get_special_icon_name (icon_type, filename);
2326             break;
2327         }
2328
2329       gtk_file_info_set_icon_name (info, icon_name);
2330
2331       if (free_icon_name)
2332         g_free ((char *) icon_name);
2333     }
2334
2335   return info;
2336 }
2337
2338 static struct stat_info_entry *
2339 create_stat_info_entry_and_emit_add (GtkFileFolderUnix *folder_unix,
2340                                      const char        *filename,
2341                                      const char        *basename,
2342                                      struct stat       *statbuf)
2343 {
2344   GSList *paths;
2345   GtkFilePath *path;
2346   struct stat_info_entry *entry;
2347
2348   entry = g_new0 (struct stat_info_entry, 1);
2349
2350   if ((folder_unix->types & STAT_NEEDED_MASK) != 0)
2351     entry->statbuf = *statbuf;
2352
2353   if ((folder_unix->types & GTK_FILE_INFO_MIME_TYPE) != 0)
2354     entry->mime_type = g_strdup (xdg_mime_get_mime_type_for_file (filename, statbuf));
2355
2356   g_hash_table_insert (folder_unix->stat_info,
2357                        g_strdup (basename),
2358                        entry);
2359
2360   path = gtk_file_path_new_dup (filename);
2361   paths = g_slist_append (NULL, path);
2362   g_signal_emit_by_name (folder_unix, "files-added", paths);
2363   gtk_file_path_free (path);
2364   g_slist_free (paths);
2365
2366   return entry;
2367 }
2368
2369 static GtkFileInfo *
2370 gtk_file_folder_unix_get_info (GtkFileFolder      *folder,
2371                                const GtkFilePath  *path,
2372                                GError            **error)
2373 {
2374   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
2375   GtkFileInfo *info;
2376   gchar *dirname, *basename;
2377   const char *filename;
2378   GtkFileInfoType types;
2379   struct stat statbuf;
2380   const char *mime_type;
2381
2382   /* Get_info for "/" */
2383   if (!path)
2384     {
2385       g_return_val_if_fail (filename_is_root (folder_unix->filename), NULL);
2386       return file_info_for_root_with_error (folder_unix->filename, error);
2387     }
2388
2389   /* Get_info for normal files */
2390
2391   filename = gtk_file_path_get_string (path);
2392   g_return_val_if_fail (filename != NULL, NULL);
2393   g_return_val_if_fail (g_path_is_absolute (filename), NULL);
2394
2395   dirname = get_parent_dir (filename);
2396   g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
2397   g_free (dirname);
2398
2399   basename = g_path_get_basename (filename);
2400   types = folder_unix->types;
2401
2402   if (folder_unix->have_stat)
2403     {
2404       struct stat_info_entry *entry;
2405
2406       g_assert (folder_unix->stat_info != NULL);
2407       entry = g_hash_table_lookup (folder_unix->stat_info, basename);
2408
2409       if (!entry)
2410         {
2411           if (!stat_with_error (filename, &statbuf, error))
2412             {
2413               g_free (basename);
2414               return NULL;
2415             }
2416
2417           entry = create_stat_info_entry_and_emit_add (folder_unix, filename, basename, &statbuf);
2418         }
2419
2420       info = create_file_info (folder_unix, filename, basename, types, &entry->statbuf, entry->mime_type);
2421       g_free (basename);
2422       return info;
2423     }
2424   else
2425     {
2426       if (!stat_with_error (filename, &statbuf, error))
2427         {
2428           g_free (basename);
2429           return NULL;
2430         }
2431
2432       if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
2433         mime_type = xdg_mime_get_mime_type_for_file (filename, &statbuf);
2434       else
2435         mime_type = NULL;
2436
2437       info = create_file_info (folder_unix, filename, basename, types, &statbuf, mime_type);
2438       g_free (basename);
2439       return info;
2440     }
2441 }
2442
2443
2444 static void
2445 cb_list_children (gpointer key, gpointer value, gpointer user_data)
2446 {
2447   GSList **children = user_data;
2448   *children = g_slist_prepend (*children, key);
2449 }
2450
2451 static gboolean
2452 gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
2453                                     GSList        **children,
2454                                     GError        **error)
2455 {
2456   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
2457   GSList *l;
2458
2459   *children = NULL;
2460
2461   /* Get the list of basenames.  */
2462   if (folder_unix->stat_info)
2463     g_hash_table_foreach (folder_unix->stat_info, cb_list_children, children);
2464
2465   /* Turn basenames into GFilePaths.  */
2466   for (l = *children; l; l = l->next)
2467     {
2468       const char *basename = l->data;
2469       char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
2470       l->data = filename_to_path (fullname);
2471       g_free (fullname);
2472     }
2473
2474   return TRUE;
2475 }
2476
2477 static gboolean
2478 gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder)
2479 {
2480   return GTK_FILE_FOLDER_UNIX (folder)->is_finished_loading;
2481 }
2482
2483 static void
2484 free_stat_info_entry (struct stat_info_entry *entry)
2485 {
2486   g_free (entry->mime_type);
2487   g_free (entry);
2488 }
2489
2490 static gboolean
2491 fill_in_names (GtkFileFolderUnix *folder_unix, GError **error)
2492 {
2493   GDir *dir;
2494
2495   if (folder_unix->stat_info)
2496     return TRUE;
2497
2498   dir = g_dir_open (folder_unix->filename, 0, error);
2499   if (!dir)
2500     return FALSE;
2501
2502   folder_unix->stat_info = g_hash_table_new_full (g_str_hash, g_str_equal,
2503                                                   (GDestroyNotify) g_free,
2504                                                   (GDestroyNotify) free_stat_info_entry);
2505
2506   while (TRUE)
2507     {
2508       struct stat_info_entry *entry;
2509       const gchar *basename;
2510
2511       basename = g_dir_read_name (dir);
2512       if (!basename)
2513         break;
2514
2515       entry = g_new0 (struct stat_info_entry, 1);
2516       if (folder_unix->is_network_dir)
2517         {
2518           entry->statbuf.st_mode = S_IFDIR;
2519           entry->mime_type = g_strdup ("x-directory/normal");
2520         }
2521
2522       g_hash_table_insert (folder_unix->stat_info,
2523                            g_strdup (basename),
2524                            entry);
2525     }
2526
2527   g_dir_close (dir);
2528
2529   folder_unix->asof = time (NULL);
2530   return TRUE;
2531 }
2532
2533 static gboolean
2534 cb_fill_in_stats (gpointer key, gpointer value, gpointer user_data)
2535 {
2536   const char *basename = key;
2537   struct stat_info_entry *entry = value;
2538   GtkFileFolderUnix *folder_unix = user_data;
2539   char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
2540   gboolean result;
2541
2542   if (stat (fullname, &entry->statbuf) == -1 &&
2543       (errno != ENOENT || lstat (fullname, &entry->statbuf) == -1))
2544     result = TRUE;  /* Couldn't stat -- remove from hash.  */
2545   else
2546     result = FALSE;
2547
2548   g_free (fullname);
2549   return result;
2550 }
2551
2552
2553 static void
2554 fill_in_stats (GtkFileFolderUnix *folder_unix)
2555 {
2556   if (folder_unix->have_stat)
2557     return;
2558
2559   if (!fill_in_names (folder_unix, NULL))
2560     return;
2561
2562   if (!folder_unix->is_network_dir)
2563     g_hash_table_foreach_remove (folder_unix->stat_info,
2564                                  cb_fill_in_stats,
2565                                  folder_unix);
2566
2567   folder_unix->have_stat = TRUE;
2568 }
2569
2570
2571 static gboolean
2572 cb_fill_in_mime_type (gpointer key, gpointer value, gpointer user_data)
2573 {
2574   const char *basename = key;
2575   struct stat_info_entry *entry = value;
2576   GtkFileFolderUnix *folder_unix = user_data;
2577   char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
2578   struct stat *statbuf = NULL;
2579   const char *mime_type;
2580
2581   if (folder_unix->have_stat)
2582     statbuf = &entry->statbuf;
2583
2584   mime_type = xdg_mime_get_mime_type_for_file (fullname, statbuf);
2585   entry->mime_type = g_strdup (mime_type);
2586
2587   g_free (fullname);
2588
2589   return FALSE;
2590 }
2591
2592 static void
2593 fill_in_mime_type (GtkFileFolderUnix *folder_unix)
2594 {
2595   if (folder_unix->have_mime_type)
2596     return;
2597
2598   if (!folder_unix->have_stat)
2599     return;
2600
2601   g_assert (folder_unix->stat_info != NULL);
2602
2603   if (!folder_unix->is_network_dir)
2604     g_hash_table_foreach_remove (folder_unix->stat_info,
2605                                  cb_fill_in_mime_type,
2606                                  folder_unix);
2607
2608   folder_unix->have_mime_type = TRUE;
2609 }
2610
2611 static gchar **
2612 read_hidden_file (const char *dirname)
2613 {
2614   gchar **lines = NULL;
2615   gchar *contents;
2616   gchar *hidden_file;
2617
2618   hidden_file = g_build_filename (dirname, HIDDEN_FILENAME, NULL);
2619
2620   if (g_file_get_contents (hidden_file, &contents, NULL, NULL))
2621     {
2622       lines = g_strsplit (contents, "\n", -1);
2623       g_free (contents);
2624     }
2625
2626   g_free (hidden_file);
2627
2628   return lines;
2629 }
2630
2631 static void
2632 fill_in_hidden (GtkFileFolderUnix *folder_unix)
2633 {
2634   gchar **lines;
2635   
2636   if (folder_unix->have_hidden)
2637     return;
2638
2639   lines = read_hidden_file (folder_unix->filename);
2640
2641   if (lines)
2642     {
2643       int i;
2644       
2645       for (i = 0; lines[i]; i++)
2646         {
2647           if (lines[i][0])
2648             {
2649               struct stat_info_entry *entry;
2650               
2651               entry = g_hash_table_lookup (folder_unix->stat_info, lines[i]);
2652               if (entry != NULL)
2653                 entry->hidden = TRUE;
2654             }
2655         }
2656       
2657       g_strfreev (lines);
2658     }
2659
2660   folder_unix->have_hidden = TRUE;
2661 }
2662
2663 static GtkFilePath *
2664 filename_to_path (const char *filename)
2665 {
2666   char *tmp;
2667
2668   tmp = remove_trailing_slash (filename);
2669   return gtk_file_path_new_steal (tmp);
2670 }
2671
2672 static gboolean
2673 filename_is_root (const char *filename)
2674 {
2675   const gchar *after_root;
2676
2677   after_root = g_path_skip_root (filename);
2678
2679   return (after_root != NULL && *after_root == '\0');
2680 }
2681
2682 static gboolean
2683 get_is_hidden_for_file (const char *filename,
2684                         const char *basename)
2685 {
2686   gchar *dirname;
2687   gchar **lines;
2688   gboolean hidden = FALSE;
2689
2690   dirname = g_path_get_dirname (filename);
2691   lines = read_hidden_file (dirname);
2692   g_free (dirname);
2693
2694   if (lines)
2695     {
2696       int i;
2697       
2698       for (i = 0; lines[i]; i++)
2699         {
2700           if (lines[i][0] && strcmp (lines[i], basename) == 0)
2701             {
2702               hidden = TRUE;
2703               break;
2704             }
2705         }
2706       
2707       g_strfreev (lines);
2708     }
2709
2710   return hidden;
2711 }
2712
2713 static gboolean
2714 file_is_hidden (GtkFileFolderUnix *folder_unix,
2715                 const char        *basename)
2716 {
2717  struct stat_info_entry *entry;
2718
2719   if (basename[0] == '.' || basename[strlen (basename) - 1] == '~')
2720     return TRUE;
2721  
2722   if (folder_unix->have_stat)
2723     {
2724       fill_in_hidden (folder_unix);
2725       
2726       entry = g_hash_table_lookup (folder_unix->stat_info, basename);
2727   
2728       if (entry)
2729         return entry->hidden;
2730     }
2731
2732   return FALSE;
2733 }
2734
2735 #define __GTK_FILE_SYSTEM_UNIX_C__
2736 #include "gtkaliasdef.c"