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