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