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