]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemwin32.c
Test that path is actually a directory and throw error if not. Fixed bug
[~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  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22 #include "gtkfilesystem.h"
23 #include "gtkfilesystemwin32.h"
24 #include "gtkintl.h"
25 #include "gtkstock.h"
26 #include "gtkiconfactory.h"
27
28 #include <errno.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <sys/types.h>
33
34 #ifdef G_OS_WIN32
35 #define WIN32_LEAN_AND_MEAN
36 #include <windows.h>
37 #include <shellapi.h> /* ExtractAssociatedIcon */
38 #include <direct.h>
39 #include <io.h>
40 #define mkdir(p,m) _mkdir(p)
41 #include <gdk/win32/gdkwin32.h> /* gdk_win32_hdc_get */
42 #else
43 #error "The implementation is win32 only."
44 #endif /* G_OS_WIN32 */
45
46 typedef struct _GtkFileSystemWin32Class GtkFileSystemWin32Class;
47
48 #define GTK_FILE_SYSTEM_WIN32_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_WIN32, GtkFileSystemWin32Class))
49 #define GTK_IS_FILE_SYSTEM_WIN32_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_WIN32))
50 #define GTK_FILE_SYSTEM_WIN32_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_WIN32, GtkFileSystemWin32Class))
51
52 struct _GtkFileSystemWin32Class
53 {
54   GObjectClass parent_class;
55 };
56
57 struct _GtkFileSystemWin32
58 {
59   GObject parent_instance;
60 };
61
62 #define GTK_TYPE_FILE_FOLDER_WIN32             (gtk_file_folder_win32_get_type ())
63 #define GTK_FILE_FOLDER_WIN32(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32))
64 #define GTK_IS_FILE_FOLDER_WIN32(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_WIN32))
65 #define GTK_FILE_FOLDER_WIN32_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32Class))
66 #define GTK_IS_FILE_FOLDER_WIN32_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_WIN32))
67 #define GTK_FILE_FOLDER_WIN32_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32Class))
68
69 typedef struct _GtkFileFolderWin32      GtkFileFolderWin32;
70 typedef struct _GtkFileFolderWin32Class GtkFileFolderWin32Class;
71
72 struct _GtkFileFolderWin32Class
73 {
74   GObjectClass parent_class;
75 };
76
77 struct _GtkFileFolderWin32
78 {
79   GObject parent_instance;
80
81   GtkFileInfoType types;
82   gchar *filename;
83 };
84
85 static GObjectClass *system_parent_class;
86 static GObjectClass *folder_parent_class;
87
88 static void           gtk_file_system_win32_class_init       (GtkFileSystemWin32Class  *class);
89 static void           gtk_file_system_win32_iface_init       (GtkFileSystemIface       *iface);
90 static void           gtk_file_system_win32_init             (GtkFileSystemWin32       *impl);
91 static void           gtk_file_system_win32_finalize         (GObject                  *object);
92
93 static GSList *       gtk_file_system_win32_list_volumes     (GtkFileSystem      *file_system);
94 static GtkFileSystemVolume *gtk_file_system_win32_get_volume_for_path (GtkFileSystem     *file_system,
95                                                                        const GtkFilePath *path);
96
97 static GtkFileFolder *gtk_file_system_win32_get_folder       (GtkFileSystem            *file_system,
98                                                               const GtkFilePath        *path,
99                                                               GtkFileInfoType           types,
100                                                               GError                  **error);
101 static gboolean       gtk_file_system_win32_create_folder    (GtkFileSystem            *file_system,
102                                                               const GtkFilePath        *path,
103                                                               GError                  **error);
104
105 static void         gtk_file_system_win32_volume_free             (GtkFileSystem       *file_system,
106                                                                    GtkFileSystemVolume *volume);
107 static GtkFilePath *gtk_file_system_win32_volume_get_base_path    (GtkFileSystem       *file_system,
108                                                                    GtkFileSystemVolume *volume);
109 static gboolean     gtk_file_system_win32_volume_get_is_mounted   (GtkFileSystem       *file_system,
110                                                                    GtkFileSystemVolume *volume);
111 static gboolean     gtk_file_system_win32_volume_mount            (GtkFileSystem       *file_system,
112                                                                    GtkFileSystemVolume *volume,
113                                                                    GError             **error);
114 static gchar *      gtk_file_system_win32_volume_get_display_name (GtkFileSystem       *file_system,
115                                                                    GtkFileSystemVolume *volume);
116 static GdkPixbuf *  gtk_file_system_win32_volume_render_icon      (GtkFileSystem        *file_system,
117                                                                    GtkFileSystemVolume  *volume,
118                                                                    GtkWidget            *widget,
119                                                                    gint                  pixel_size,
120                                                                    GError              **error);
121
122 static gboolean       gtk_file_system_win32_get_parent       (GtkFileSystem            *file_system,
123                                                               const GtkFilePath        *path,
124                                                               GtkFilePath             **parent,
125                                                               GError                  **error);
126 static GtkFilePath *  gtk_file_system_win32_make_path        (GtkFileSystem            *file_system,
127                                                               const GtkFilePath        *base_path,
128                                                               const gchar              *display_name,
129                                                               GError                  **error);
130 static gboolean       gtk_file_system_win32_parse            (GtkFileSystem            *file_system,
131                                                               const GtkFilePath        *base_path,
132                                                               const gchar              *str,
133                                                               GtkFilePath             **folder,
134                                                               gchar                   **file_part,
135                                                               GError                  **error);
136 static gchar *        gtk_file_system_win32_path_to_uri      (GtkFileSystem            *file_system,
137                                                               const GtkFilePath        *path);
138 static gchar *        gtk_file_system_win32_path_to_filename (GtkFileSystem            *file_system,
139                                                               const GtkFilePath        *path);
140 static GtkFilePath *  gtk_file_system_win32_uri_to_path      (GtkFileSystem            *file_system,
141                                                               const gchar              *uri);
142 static GtkFilePath *  gtk_file_system_win32_filename_to_path (GtkFileSystem            *file_system,
143                                                               const gchar              *filename);
144 static GdkPixbuf *gtk_file_system_win32_render_icon (GtkFileSystem     *file_system,
145                                                      const GtkFilePath *path,
146                                                      GtkWidget         *widget,
147                                                      gint               pixel_size,
148                                                      GError           **error);
149
150 static gboolean       gtk_file_system_win32_insert_bookmark  (GtkFileSystem            *file_system,
151                                                               const GtkFilePath        *path,
152                                                               gint               position,
153                                                               GError                  **error);
154 static gboolean       gtk_file_system_win32_remove_bookmark  (GtkFileSystem            *file_system,
155                                                               const GtkFilePath        *path,
156                                                               GError                  **error);
157 static GSList *       gtk_file_system_win32_list_bookmarks   (GtkFileSystem            *file_system);
158 static GType          gtk_file_folder_win32_get_type         (void);
159 static void           gtk_file_folder_win32_class_init       (GtkFileFolderWin32Class  *class);
160 static void           gtk_file_folder_win32_iface_init       (GtkFileFolderIface       *iface);
161 static void           gtk_file_folder_win32_init             (GtkFileFolderWin32       *impl);
162 static void           gtk_file_folder_win32_finalize         (GObject                  *object);
163 static GtkFileInfo *  gtk_file_folder_win32_get_info         (GtkFileFolder            *folder,
164                                                               const GtkFilePath        *path,
165                                                               GError                  **error);
166 static gboolean       gtk_file_folder_win32_list_children    (GtkFileFolder            *folder,
167                                                               GSList                  **children,
168                                                               GError                  **error);
169
170 static gchar *        filename_from_path                     (const GtkFilePath        *path);
171 static GtkFilePath *  filename_to_path                       (const gchar              *filename);
172
173 static gboolean       filename_is_root                       (const char               *filename);
174 static GtkFileInfo *  filename_get_info                      (const gchar              *filename,
175                                                               GtkFileInfoType           types,
176                                                               GError                  **error);
177
178 /* some info kept together for volumes */
179 struct _GtkFileSystemVolume
180 {
181   gchar    *drive;
182   gboolean  is_mounted;
183 };
184
185 /*
186  * GtkFileSystemWin32
187  */
188 GType
189 gtk_file_system_win32_get_type (void)
190 {
191   static GType file_system_win32_type = 0;
192
193   if (!file_system_win32_type)
194     {
195       static const GTypeInfo file_system_win32_info =
196       {
197         sizeof (GtkFileSystemWin32Class),
198         NULL,           /* base_init */
199         NULL,           /* base_finalize */
200         (GClassInitFunc) gtk_file_system_win32_class_init,
201         NULL,           /* class_finalize */
202         NULL,           /* class_data */
203         sizeof (GtkFileSystemWin32),
204         0,              /* n_preallocs */
205         (GInstanceInitFunc) gtk_file_system_win32_init,
206       };
207       
208       static const GInterfaceInfo file_system_info =
209       {
210         (GInterfaceInitFunc) gtk_file_system_win32_iface_init, /* interface_init */
211         NULL,                                                  /* interface_finalize */
212         NULL                                                   /* interface_data */
213       };
214
215       file_system_win32_type = g_type_register_static (G_TYPE_OBJECT,
216                                                        "GtkFileSystemWin32",
217                                                        &file_system_win32_info, 0);
218       g_type_add_interface_static (file_system_win32_type,
219                                    GTK_TYPE_FILE_SYSTEM,
220                                    &file_system_info);
221     }
222
223   return file_system_win32_type;
224 }
225
226 /**
227  * gtk_file_system_win32_new:
228  * 
229  * Creates a new #GtkFileSystemWin32 object. #GtkFileSystemWin32
230  * implements the #GtkFileSystem interface with direct access to
231  * the filesystem using Windows API calls
232  * 
233  * Return value: the new #GtkFileSystemWin32 object
234  **/
235 GtkFileSystem *
236 gtk_file_system_win32_new (void)
237 {
238   return g_object_new (GTK_TYPE_FILE_SYSTEM_WIN32, NULL);
239 }
240
241 static void
242 gtk_file_system_win32_class_init (GtkFileSystemWin32Class *class)
243 {
244   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
245
246   system_parent_class = g_type_class_peek_parent (class);
247   
248   gobject_class->finalize = gtk_file_system_win32_finalize;
249 }
250
251 static void
252 gtk_file_system_win32_iface_init (GtkFileSystemIface *iface)
253 {
254   iface->list_volumes = gtk_file_system_win32_list_volumes;
255   iface->get_volume_for_path = gtk_file_system_win32_get_volume_for_path;
256   iface->get_folder = gtk_file_system_win32_get_folder;
257   iface->create_folder = gtk_file_system_win32_create_folder;
258   iface->volume_free = gtk_file_system_win32_volume_free;
259   iface->volume_get_base_path = gtk_file_system_win32_volume_get_base_path;
260   iface->volume_get_is_mounted = gtk_file_system_win32_volume_get_is_mounted;
261   iface->volume_mount = gtk_file_system_win32_volume_mount;
262   iface->volume_get_display_name = gtk_file_system_win32_volume_get_display_name;
263   iface->volume_render_icon = gtk_file_system_win32_volume_render_icon;
264   iface->get_parent = gtk_file_system_win32_get_parent;
265   iface->make_path = gtk_file_system_win32_make_path;
266   iface->parse = gtk_file_system_win32_parse;
267   iface->path_to_uri = gtk_file_system_win32_path_to_uri;
268   iface->path_to_filename = gtk_file_system_win32_path_to_filename;
269   iface->uri_to_path = gtk_file_system_win32_uri_to_path;
270   iface->filename_to_path = gtk_file_system_win32_filename_to_path;
271   iface->render_icon = gtk_file_system_win32_render_icon;
272   iface->insert_bookmark = gtk_file_system_win32_insert_bookmark;
273   iface->remove_bookmark = gtk_file_system_win32_remove_bookmark;
274   iface->list_bookmarks = gtk_file_system_win32_list_bookmarks;
275 }
276
277 static void
278 gtk_file_system_win32_init (GtkFileSystemWin32 *system_win32)
279 {
280 }
281
282 static void
283 gtk_file_system_win32_finalize (GObject *object)
284 {
285   system_parent_class->finalize (object);
286 }
287
288 static GSList *
289 gtk_file_system_win32_list_volumes (GtkFileSystem *file_system)
290 {
291   DWORD   drives;
292   gchar   drive[4] = "A:\\";
293   GSList *list = NULL;
294
295   drives = GetLogicalDrives();
296
297   if (!drives)
298     g_warning ("GetLogicalDrives failed.");
299
300   while (drives && drive[0] <= 'Z')
301     {
302       if (drives & 1)
303       {
304         GtkFileSystemVolume *vol = g_new0 (GtkFileSystemVolume, 1);
305         if (drive[0] == 'A' || drive[0] == 'B')
306           vol->is_mounted = FALSE; /* skip floppy */
307         else
308           vol->is_mounted = TRUE; /* handle other removable drives special, too? */
309
310         vol->drive = g_strdup (drive);
311         list = g_slist_append (list, vol);
312       }
313       drives >>= 1;
314       drive[0]++;
315     }
316   return list;
317 }
318
319 static GtkFileSystemVolume *
320 gtk_file_system_win32_get_volume_for_path (GtkFileSystem     *file_system,
321                                            const GtkFilePath *path)
322 {
323   GtkFileSystemVolume *vol = g_new0 (GtkFileSystemVolume, 1);
324   gchar* p = g_strndup (gtk_file_path_get_string (path), 3);
325
326   g_return_val_if_fail (p != NULL, NULL);
327
328   /*FIXME: gtk_file_path_compare() is case sensitive, we are not*/
329   p[0] = g_ascii_toupper (p[0]);
330   vol->drive = p;
331   vol->is_mounted = (p[0] != 'A' && p[0] != 'B');
332
333   return vol;
334 }
335
336 static GtkFileFolder *
337 gtk_file_system_win32_get_folder (GtkFileSystem    *file_system,
338                                  const GtkFilePath *path,
339                                  GtkFileInfoType    types,
340                                  GError           **error)
341 {
342   GtkFileFolderWin32 *folder_win32;
343   gchar *filename;
344
345   filename = filename_from_path (path);
346   g_return_val_if_fail (filename != NULL, NULL);
347
348   if (!g_file_test (filename, G_FILE_TEST_IS_DIR))
349     {
350       int save_errno = errno;
351       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
352
353       /* If g_file_test() returned FALSE but not due to an error, it means
354        * that the filename is not a directory.
355        */
356       if (save_errno == 0)
357         /* ENOTDIR */
358         g_set_error (error,
359                      GTK_FILE_SYSTEM_ERROR,
360                      GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
361                      _("%s: %s"),
362                      filename_utf8 ? filename_utf8 : "???",
363                      g_strerror (ENOTDIR));
364       else
365         g_set_error (error,
366                      GTK_FILE_SYSTEM_ERROR,
367                      GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
368                      _("error getting information for '%s': %s"),
369                      filename_utf8 ? filename_utf8 : "???",
370                      g_strerror (save_errno));
371
372       g_free (filename_utf8);
373       return NULL;
374     }
375
376   folder_win32 = g_object_new (GTK_TYPE_FILE_FOLDER_WIN32, NULL);
377   folder_win32->filename = filename;
378   folder_win32->types = types;
379
380   return GTK_FILE_FOLDER (folder_win32);
381 }
382
383 static gboolean
384 gtk_file_system_win32_create_folder (GtkFileSystem     *file_system,
385                                      const GtkFilePath *path,
386                                      GError           **error)
387 {
388   gchar *filename;
389   gboolean result;
390
391   filename = filename_from_path (path);
392   g_return_val_if_fail (filename != NULL, FALSE);
393   
394   result = mkdir (filename, 0777) != 0;
395   
396   if (!result)
397     {
398       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
399       g_set_error (error,
400                    GTK_FILE_SYSTEM_ERROR,
401                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
402                    _("error creating directory '%s': %s"),
403                    filename_utf8 ? filename_utf8 : "???",
404                    g_strerror (errno));
405       g_free (filename_utf8);
406     }
407   
408   g_free (filename);
409   
410   return result;
411 }
412
413 static void
414 gtk_file_system_win32_volume_free (GtkFileSystem        *file_system,
415                                    GtkFileSystemVolume  *volume)
416 {
417   g_free (volume->drive);
418   g_free (volume);
419 }
420
421 static GtkFilePath *
422 gtk_file_system_win32_volume_get_base_path (GtkFileSystem        *file_system,
423                                             GtkFileSystemVolume  *volume)
424 {
425   return (GtkFilePath *) g_strdup (volume->drive);
426 }
427
428 static gboolean
429 gtk_file_system_win32_volume_get_is_mounted (GtkFileSystem        *file_system,
430                                              GtkFileSystemVolume  *volume)
431 {
432   return volume->is_mounted;
433 }
434
435 static gboolean
436 gtk_file_system_win32_volume_mount (GtkFileSystem        *file_system, 
437                                     GtkFileSystemVolume  *volume,
438                                     GError              **error)
439 {
440   g_set_error (error,
441                GTK_FILE_SYSTEM_ERROR,
442                GTK_FILE_SYSTEM_ERROR_FAILED,
443                _("This file system does not support mounting"));
444   return FALSE;
445 }
446
447 static gchar *
448 gtk_file_system_win32_volume_get_display_name (GtkFileSystem       *file_system,
449                                               GtkFileSystemVolume *volume)
450 {
451   gchar *real_display_name;
452   gunichar2 *wdrive = g_utf8_to_utf16 (volume->drive, -1, NULL, NULL, NULL);
453   gunichar2  wname[80];
454
455   g_return_val_if_fail (wdrive != NULL, NULL);
456
457   if (GetVolumeInformationW (wdrive,
458                             wname, G_N_ELEMENTS(wname), 
459                             NULL, /* serial number */
460                             NULL, /* max. component length */
461                             NULL, /* fs flags */
462                             NULL, 0) /* fs type like FAT, NTFS */
463                             && wname[0])
464     {
465       gchar *name = g_utf16_to_utf8 (wname, -1, NULL, NULL, NULL);
466       real_display_name = g_strconcat (name, " (", volume->drive, ")", NULL);
467       g_free (name);
468     }
469   else
470     real_display_name = g_strdup (volume->drive);
471
472   g_free (wdrive);
473
474   return real_display_name;
475 }
476
477 static GdkPixbuf *
478 gtk_file_system_win32_volume_render_icon (GtkFileSystem        *file_system,
479                                          GtkFileSystemVolume  *volume,
480                                          GtkWidget            *widget,
481                                          gint                  pixel_size,
482                                          GError              **error)
483 {
484   GtkIconSet *icon_set = NULL;
485   DWORD dt = GetDriveType (volume->drive);
486
487   switch (dt)
488     {
489     case DRIVE_REMOVABLE :
490       icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_FLOPPY);
491       break;
492     case DRIVE_CDROM :
493       icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_CDROM);
494       break;
495     case DRIVE_REMOTE :
496       icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_NETWORK);
497       break;
498     case DRIVE_FIXED :
499       icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_HARDDISK);
500       break;
501     case DRIVE_RAMDISK :
502       /*FIXME: need a ram stock icon
503       gtk_file_info_set_icon_type (info, GTK_STOCK_OPEN);*/
504       break;
505     default :
506       g_assert_not_reached ();
507     }
508
509   return gtk_icon_set_render_icon (icon_set, 
510                                    widget->style,
511                                    gtk_widget_get_direction (widget),
512                                    GTK_STATE_NORMAL,
513                                    GTK_ICON_SIZE_BUTTON,
514                                    widget, NULL); 
515 }
516
517 static gboolean
518 gtk_file_system_win32_get_parent (GtkFileSystem     *file_system,
519                                   const GtkFilePath *path,
520                                   GtkFilePath      **parent,
521                                   GError           **error)
522 {
523   gchar *filename = filename_from_path (path);
524   g_return_val_if_fail (filename != NULL, FALSE);
525
526   if (filename_is_root (filename))
527     {
528       *parent = NULL;
529     }
530   else
531     {
532       gchar *parent_filename = g_path_get_dirname (filename);
533       *parent = filename_to_path (parent_filename);
534       g_free (parent_filename);
535     }
536
537   g_free (filename);
538
539   return TRUE;
540 }
541
542 static GtkFilePath *
543 gtk_file_system_win32_make_path (GtkFileSystem     *file_system,
544                                  const GtkFilePath *base_path,
545                                  const gchar       *display_name,
546                                  GError           **error)
547 {
548   const char *base_filename;
549   gchar *filename;
550   gchar *full_filename;
551   GError *tmp_error = NULL;
552   GtkFilePath *result;
553   
554   base_filename = gtk_file_path_get_string (base_path);
555   g_return_val_if_fail (base_filename != NULL, NULL);
556   g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
557
558   filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
559   if (!filename)
560     {
561       g_set_error (error,
562                    GTK_FILE_SYSTEM_ERROR,
563                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
564                    "%s",
565                    tmp_error->message);
566       
567       g_error_free (tmp_error);
568
569       return NULL;
570     }
571     
572   full_filename = g_build_filename (base_filename, filename, NULL);
573   result = filename_to_path (full_filename);
574   g_free (filename);
575   g_free (full_filename);
576   
577   return result;
578 }
579
580 /* If this was a publically exported function, it should return
581  * a dup'ed result, but we make it modify-in-place for efficiency
582  * here, and because it works for us.
583  */
584 static void
585 canonicalize_filename (gchar *filename)
586 {
587   gchar *p, *q;
588   gboolean last_was_slash = FALSE;
589
590   p = filename;
591   q = filename;
592
593   while (*p)
594     {
595       if (*p == G_DIR_SEPARATOR)
596         {
597           if (!last_was_slash)
598             *q++ = G_DIR_SEPARATOR;
599
600           last_was_slash = TRUE;
601         }
602       else
603         {
604           if (last_was_slash && *p == '.')
605             {
606               if (*(p + 1) == G_DIR_SEPARATOR ||
607                   *(p + 1) == '\0')
608                 {
609                   if (*(p + 1) == '\0')
610                     break;
611                   
612                   p += 1;
613                 }
614               else if (*(p + 1) == '.' &&
615                        (*(p + 2) == G_DIR_SEPARATOR ||
616                         *(p + 2) == '\0'))
617                 {
618                   if (q > filename + 1)
619                     {
620                       q--;
621                       while (q > filename + 1 &&
622                              *(q - 1) != G_DIR_SEPARATOR)
623                         q--;
624                     }
625
626                   if (*(p + 2) == '\0')
627                     break;
628                   
629                   p += 2;
630                 }
631               else
632                 {
633                   *q++ = *p;
634                   last_was_slash = FALSE;
635                 }
636             }
637           else
638             {
639               *q++ = *p;
640               last_was_slash = FALSE;
641             }
642         }
643
644       p++;
645     }
646
647   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
648     q--;
649
650   *q = '\0';
651 }
652
653 static gboolean
654 gtk_file_system_win32_parse (GtkFileSystem     *file_system,
655                              const GtkFilePath *base_path,
656                              const gchar       *str,
657                              GtkFilePath      **folder,
658                              gchar            **file_part,
659                              GError           **error)
660 {
661   const char *base_filename;
662   gchar *last_slash;
663   gboolean result = FALSE;
664
665   base_filename = gtk_file_path_get_string (base_path);
666   g_return_val_if_fail (base_filename != NULL, FALSE);
667   g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
668   
669   last_slash = strrchr (str, G_DIR_SEPARATOR);
670   if (!last_slash)
671     {
672       *folder = gtk_file_path_copy (base_path);
673       *file_part = g_strdup (str);
674       result = TRUE;
675     }
676   else
677     {
678       gchar *folder_part;
679       gchar *folder_path;
680       GError *tmp_error = NULL;
681
682       if (last_slash == str)
683         folder_part = g_strdup ("/");
684       else
685         folder_part = g_filename_from_utf8 (str, last_slash - str,
686                                             NULL, NULL, &tmp_error);
687
688       if (!folder_part)
689         {
690           g_set_error (error,
691                        GTK_FILE_SYSTEM_ERROR,
692                        GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
693                        "%s",
694                        tmp_error->message);
695           g_error_free (tmp_error);
696         }
697       else
698         {
699           if (folder_part[1] == ':')
700             folder_path = folder_part;
701           else
702             {
703               folder_path = g_build_filename (base_filename, folder_part, NULL);
704               g_free (folder_part);
705             }
706
707           canonicalize_filename (folder_path);
708           
709           *folder = filename_to_path (folder_path);
710           *file_part = g_strdup (last_slash + 1);
711
712           g_free (folder_path);
713
714           result = TRUE;
715         }
716     }
717
718   return result;
719 }
720
721 static gchar *
722 gtk_file_system_win32_path_to_uri (GtkFileSystem     *file_system,
723                                    const GtkFilePath *path)
724 {
725   return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
726 }
727
728 static gchar *
729 gtk_file_system_win32_path_to_filename (GtkFileSystem     *file_system,
730                                         const GtkFilePath *path)
731 {
732   return g_strdup (gtk_file_path_get_string (path));
733 }
734
735 static GtkFilePath *
736 gtk_file_system_win32_uri_to_path (GtkFileSystem     *file_system,
737                                    const gchar       *uri)
738 {
739   gchar *filename = g_filename_from_uri (uri, NULL, NULL);
740   if (filename)
741     return gtk_file_path_new_steal (filename);
742   else
743     return NULL;
744 }
745
746 static GtkFilePath *
747 gtk_file_system_win32_filename_to_path (GtkFileSystem *file_system,
748                                         const gchar   *filename)
749 {
750   return gtk_file_path_new_dup (filename);
751 }
752
753 static gboolean
754 bookmarks_serialize (GSList  **bookmarks,
755                      gchar    *uri,
756                      gboolean  add,
757                      gint      position,
758                      GError  **error)
759 {
760   gchar   *filename;
761   gboolean ok = TRUE;
762   GSList  *list = *bookmarks;
763
764   filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
765
766   if (filename)
767     {
768       gchar *contents = NULL;
769       gsize  len = 0;
770       GSList *entry;
771       FILE  *f;   
772        
773       if (g_file_test (filename, G_FILE_TEST_EXISTS))
774         {
775           if (g_file_get_contents (filename, &contents, &len, error))
776             {
777               gchar **lines = g_strsplit (contents, "\n", -1);
778               gint    i;
779
780               for (i = 0; lines[i] != NULL; i++)
781                 {
782                   if (lines[i][0] && !g_slist_find_custom (list, lines[i], (GCompareFunc) strcmp))
783                     list = g_slist_append (list, g_strdup (lines[i]));
784                 }
785               g_strfreev (lines);
786             }
787           else
788             ok = FALSE;
789         }
790       if (ok && (f = fopen (filename, "wb")) != NULL)
791         {
792           entry = g_slist_find_custom (list, uri, (GCompareFunc) strcmp);
793           if (add)
794             {
795               /* g_slist_insert() and our insert semantics are 
796                * compatible, but maybe we should check for 
797                * positon > length ?
798                * 
799                */
800               if (!entry)
801                 list = g_slist_insert (list, g_strdup (uri), position);
802               else
803                 {
804                   g_set_error (error,
805                                GTK_FILE_SYSTEM_ERROR,
806                                GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
807                                "%s already exists in the bookmarks list",
808                                uri);
809                   ok = FALSE;
810                 }
811             }
812           else
813             {
814               /* to remove the given uri */
815               if (entry)
816                 list = g_slist_delete_link(list, entry);
817               for (entry = list; entry != NULL; entry = entry->next)
818                 {
819                   fputs (entry->data, f);
820                   fputs ("\n", f);
821                 }
822             }
823           fclose (f);
824         }
825       else if (ok && error)
826         {
827           g_set_error (error,
828                        GTK_FILE_SYSTEM_ERROR,
829                        GTK_FILE_SYSTEM_ERROR_FAILED,
830                        _("Bookmark saving failed (%s)"),
831                        g_strerror (errno));
832         }
833     }
834   *bookmarks = list;
835   return ok;
836 }
837
838 static GdkPixbuf*
839 extract_icon (const char* filename)
840 {
841   GdkPixbuf *pixbuf = NULL;
842   WORD iicon;
843   HICON hicon;
844   
845   if (!filename || !filename[0])
846     return NULL;
847
848   hicon = ExtractAssociatedIcon (GetModuleHandle (NULL), filename, &iicon);
849   if (hicon > (HICON)1)
850     {
851       ICONINFO ii;
852
853       if (GetIconInfo (hicon, &ii))
854         {
855           SIZE   size;
856           GdkPixmap *pixmap;
857           GdkGC *gc;
858           HDC    hdc;
859
860           if (!GetBitmapDimensionEx (ii.hbmColor, &size))
861             g_warning ("GetBitmapDimensionEx failed.");
862
863           if (size.cx < 1) size.cx = 32;
864           if (size.cy < 1) size.cy = 32;
865             
866           pixmap = gdk_pixmap_new (NULL, size.cx, size.cy, 
867                                    gdk_screen_get_system_visual (gdk_screen_get_default ())->depth);
868           gc = gdk_gc_new (pixmap);
869           hdc = gdk_win32_hdc_get (GDK_DRAWABLE (pixmap), gc, 0);
870
871           if (!DrawIcon (hdc, 0, 0, hicon))
872             g_warning ("DrawIcon failed");
873
874           gdk_win32_hdc_release (GDK_DRAWABLE (pixmap), gc, 0);
875
876           pixbuf = gdk_pixbuf_get_from_drawable (
877                      NULL, pixmap, 
878                      gdk_screen_get_system_colormap (gdk_screen_get_default ()),
879                      0, 0, 0, 0, size.cx, size.cy);
880           g_object_unref (pixmap);
881           g_object_unref (gc);
882         }
883       else
884         g_print ("GetIconInfo failed: %s\n", g_win32_error_message (GetLastError ())); 
885
886       if (!DestroyIcon (hicon))
887         g_warning ("DestroyIcon failed");
888     }
889   else
890     g_print ("ExtractAssociatedIcon failed: %s\n", g_win32_error_message (GetLastError ()));
891
892   return pixbuf;
893 }
894
895 static GtkIconSet *
896 win32_pseudo_mime_lookup (const char* name)
897 {
898   static GHashTable *mime_hash = NULL;
899   GtkIconSet *is = NULL;
900   char *p = strrchr(name, '.');
901   char *extension = p ? g_ascii_strdown (p, -1) : g_strdup ("");
902
903   if (!mime_hash)
904     mime_hash = g_hash_table_new (g_str_hash, g_str_equal);
905
906   /* do we already have it ? */
907   is = g_hash_table_lookup (mime_hash, extension);
908   if (is)
909     {
910       g_free (extension);
911       return is;
912     }
913   /* create icon and set */
914   {
915     GdkPixbuf *pixbuf = extract_icon (name);
916     if (pixbuf)
917       {
918         GtkIconSource* source = gtk_icon_source_new ();
919
920         is = gtk_icon_set_new_from_pixbuf (pixbuf);
921         gtk_icon_source_set_pixbuf (source, pixbuf);
922         gtk_icon_set_add_source (is, source);
923
924         gtk_icon_source_free (source);
925       }
926
927     g_hash_table_insert (mime_hash, extension, is);
928     return is;
929   }
930 }
931
932 static GdkPixbuf *
933 gtk_file_system_win32_render_icon (GtkFileSystem     *file_system,
934                                    const GtkFilePath *path,
935                                    GtkWidget         *widget,
936                                    gint               pixel_size,
937                                    GError           **error)
938 {
939   GtkIconSet *icon_set = NULL;
940   const char* filename = gtk_file_path_get_string (path);
941
942   /* handle drives with stock icons */
943   if (filename_is_root (filename))
944     {
945       gchar *filename2 = g_strconcat(filename, "\\", NULL);
946       DWORD dt = GetDriveType (filename2);
947
948       switch (dt)
949         {
950         case DRIVE_REMOVABLE :
951           icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_FLOPPY);
952           break;
953         case DRIVE_CDROM :
954           icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_CDROM);
955           break;
956         case DRIVE_FIXED : /* need a hard disk icon */
957           icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_HARDDISK);
958           break;
959         default :
960           break;
961         }
962       g_free (filename2);
963     }
964   else if (g_file_test (filename, G_FILE_TEST_IS_DIR))
965     {
966       if (0 == strcmp (g_get_home_dir(), filename))
967         icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_HOME);
968       else
969         icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_OPEN);
970     }
971   else if (g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE))
972     {
973       /* don't lookup all executable icons */
974       icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_EXECUTE);
975     }
976   else if (g_file_test (filename, G_FILE_TEST_EXISTS))
977     {
978       icon_set = win32_pseudo_mime_lookup (filename);
979     }
980
981   if (!icon_set)
982     {
983        g_set_error (error,
984                   GTK_FILE_SYSTEM_ERROR,
985                   GTK_FILE_SYSTEM_ERROR_FAILED,
986                   _("This file system does not support icons for everything"));
987        return NULL;
988     }
989
990   // FIXME : I'd like to get from pixel_size (=20) back to
991   // icon size, which is an index, but there appears to be no way ?
992   return gtk_icon_set_render_icon (icon_set, 
993                                    widget->style,
994                                    gtk_widget_get_direction (widget),
995                                    GTK_STATE_NORMAL,
996                                    GTK_ICON_SIZE_BUTTON,
997                                    widget, NULL); 
998 }
999
1000 static GSList *_bookmarks = NULL;
1001
1002 static gboolean
1003 gtk_file_system_win32_insert_bookmark (GtkFileSystem     *file_system,
1004                                     const GtkFilePath *path,
1005                                     gint               position,
1006                                     GError           **error)
1007 {
1008   gchar *uri = gtk_file_system_win32_path_to_uri (file_system, path);
1009   gboolean ret = bookmarks_serialize (&_bookmarks, uri, TRUE, position, error);
1010   if (ret)
1011     g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1012   g_free (uri);
1013   return ret;
1014                                
1015 }
1016
1017 static gboolean
1018 gtk_file_system_win32_remove_bookmark (GtkFileSystem     *file_system,
1019                                        const GtkFilePath *path,
1020                                        GError           **error)
1021 {
1022   gchar *uri = gtk_file_system_win32_path_to_uri (file_system, path);
1023   gboolean ret = bookmarks_serialize (&_bookmarks, uri, FALSE, 0, error);
1024   if (ret)
1025     g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1026   g_free (uri);
1027   return ret;
1028 }
1029
1030 static GSList *
1031 gtk_file_system_win32_list_bookmarks (GtkFileSystem *file_system)
1032 {
1033   GSList *list = NULL;
1034   GSList *entry;
1035
1036
1037   if (bookmarks_serialize (&_bookmarks, "", FALSE, 0, NULL))
1038     {
1039       for (entry = _bookmarks; entry != NULL; entry = entry->next)
1040         {
1041           GtkFilePath *path = gtk_file_system_win32_uri_to_path (
1042                                 file_system, (gchar *)entry->data);
1043
1044           list = g_slist_append (list, path);
1045         }
1046     }
1047
1048   return list;
1049 }
1050
1051 /*
1052  * GtkFileFolderWin32
1053  */
1054 static GType
1055 gtk_file_folder_win32_get_type (void)
1056 {
1057   static GType file_folder_win32_type = 0;
1058
1059   if (!file_folder_win32_type)
1060     {
1061       static const GTypeInfo file_folder_win32_info =
1062       {
1063         sizeof (GtkFileFolderWin32Class),
1064         NULL,           /* base_init */
1065         NULL,           /* base_finalize */
1066         (GClassInitFunc) gtk_file_folder_win32_class_init,
1067         NULL,           /* class_finalize */
1068         NULL,           /* class_data */
1069         sizeof (GtkFileFolderWin32),
1070         0,              /* n_preallocs */
1071         (GInstanceInitFunc) gtk_file_folder_win32_init,
1072       };
1073       
1074       static const GInterfaceInfo file_folder_info =
1075       {
1076         (GInterfaceInitFunc) gtk_file_folder_win32_iface_init, /* interface_init */
1077         NULL,                                                 /* interface_finalize */
1078         NULL                                                  /* interface_data */
1079       };
1080
1081       file_folder_win32_type = g_type_register_static (G_TYPE_OBJECT,
1082                                                       "GtkFileFolderWin32",
1083                                                       &file_folder_win32_info, 0);
1084       g_type_add_interface_static (file_folder_win32_type,
1085                                    GTK_TYPE_FILE_FOLDER,
1086                                    &file_folder_info);
1087     }
1088
1089   return file_folder_win32_type;
1090 }
1091
1092 static void
1093 gtk_file_folder_win32_class_init (GtkFileFolderWin32Class *class)
1094 {
1095   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1096
1097   folder_parent_class = g_type_class_peek_parent (class);
1098
1099   gobject_class->finalize = gtk_file_folder_win32_finalize;
1100 }
1101
1102 static void
1103 gtk_file_folder_win32_iface_init (GtkFileFolderIface *iface)
1104 {
1105   iface->get_info = gtk_file_folder_win32_get_info;
1106   iface->list_children = gtk_file_folder_win32_list_children;
1107 }
1108
1109 static void
1110 gtk_file_folder_win32_init (GtkFileFolderWin32 *impl)
1111 {
1112 }
1113
1114 static void
1115 gtk_file_folder_win32_finalize (GObject *object)
1116 {
1117   GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (object);
1118
1119   g_free (folder_win32->filename);
1120   
1121   folder_parent_class->finalize (object);
1122 }
1123
1124 static GtkFileInfo *
1125 gtk_file_folder_win32_get_info (GtkFileFolder     *folder,
1126                                 const GtkFilePath *path,
1127                                 GError           **error)
1128 {
1129   GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (folder);
1130   GtkFileInfo *info;
1131   gchar *dirname;
1132   gchar *filename;
1133   
1134   if (!path)
1135     {
1136       g_return_val_if_fail (filename_is_root (folder_win32->filename), NULL);
1137
1138       /* ??? */
1139       info = filename_get_info (folder_win32->filename, folder_win32->types, error);
1140
1141       return info;
1142     }
1143
1144   filename = filename_from_path (path);
1145   g_return_val_if_fail (filename != NULL, NULL);
1146
1147 #if 0
1148   dirname = g_path_get_dirname (filename);
1149   g_return_val_if_fail (strcmp (dirname, folder_win32->filename) == 0, NULL);
1150   g_free (dirname);
1151 #endif
1152
1153   info = filename_get_info (filename, folder_win32->types, error);
1154
1155   g_free (filename);
1156
1157   return info;
1158 }
1159
1160 static gboolean
1161 gtk_file_folder_win32_list_children (GtkFileFolder  *folder,
1162                                      GSList        **children,
1163                                      GError        **error)
1164 {
1165   GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (folder);
1166   GError *tmp_error = NULL;
1167   GDir *dir;
1168
1169   *children = NULL;
1170
1171   dir = g_dir_open (folder_win32->filename, 0, &tmp_error);
1172   if (!dir)
1173     {
1174       g_set_error (error,
1175                    GTK_FILE_SYSTEM_ERROR,
1176                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1177                    "%s",
1178                    tmp_error->message);
1179       
1180       g_error_free (tmp_error);
1181
1182       return FALSE;
1183     }
1184
1185   while (TRUE)
1186     {
1187       const gchar *filename = g_dir_read_name (dir);
1188       gchar *fullname;
1189
1190       if (!filename)
1191         break;
1192
1193       fullname = g_build_filename (folder_win32->filename, filename, NULL);
1194       *children = g_slist_prepend (*children, filename_to_path (fullname));
1195       g_free (fullname);
1196     }
1197
1198   g_dir_close (dir);
1199
1200   *children = g_slist_reverse (*children);
1201   
1202   return TRUE;
1203 }
1204
1205 static GtkFileInfo *
1206 filename_get_info (const gchar     *filename,
1207                    GtkFileInfoType  types,
1208                    GError         **error)
1209 {
1210   GtkFileInfo *info;
1211 #if 0 /* it's dead in GtkFileSystemUnix.c, too */
1212   GtkFileIconType icon_type = GTK_FILE_ICON_REGULAR;
1213 #endif
1214   WIN32_FILE_ATTRIBUTE_DATA wfad;
1215
1216   if (!GetFileAttributesEx (filename, GetFileExInfoStandard, &wfad))
1217     {
1218       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1219       g_set_error (error,
1220                    GTK_FILE_SYSTEM_ERROR,
1221                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1222                    _("error getting information for '%s': %s"),
1223                    filename_utf8 ? filename_utf8 : "???",
1224                    g_win32_error_message (GetLastError ()));
1225       g_free (filename_utf8);
1226       
1227       return NULL;
1228     }
1229
1230   info = gtk_file_info_new ();
1231
1232   if (filename_is_root (filename))
1233     {
1234       if (types & GTK_FILE_INFO_DISPLAY_NAME)
1235         gtk_file_info_set_display_name (info, filename);
1236       
1237       if (types & GTK_FILE_INFO_IS_HIDDEN)
1238         gtk_file_info_set_is_hidden (info, FALSE);
1239     }
1240   else
1241     {
1242       gchar *basename = g_path_get_basename (filename);
1243   
1244       if (types & GTK_FILE_INFO_DISPLAY_NAME)
1245         {
1246           gchar *display_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1247           if (!display_name)
1248             display_name = g_strescape (basename, NULL);
1249           
1250           gtk_file_info_set_display_name (info, display_name);
1251           
1252           g_free (display_name);
1253         }
1254       
1255       if (types & GTK_FILE_INFO_IS_HIDDEN)
1256         {
1257             /* win32 convention ... */
1258             gboolean is_hidden = basename[0] == '.';
1259             /* ... _and_ windoze attribute */
1260             is_hidden = is_hidden || !!(wfad.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
1261           gtk_file_info_set_is_hidden (info, is_hidden);
1262         }
1263
1264       g_free (basename);
1265     }
1266
1267   if (types & GTK_FILE_INFO_IS_FOLDER)
1268     {
1269       gtk_file_info_set_is_folder (info, !!(wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
1270    }
1271
1272 #if 0 /* it's dead in GtkFileSystemUnix.c, too */
1273   if (types & GTK_FILE_INFO_ICON)
1274     {
1275       if (wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1276         icon_type = GTK_FILE_ICON_DIRECTORY;
1277
1278       gtk_file_info_set_icon_type (info, icon_type);
1279     }
1280 #endif
1281
1282   if ((types & GTK_FILE_INFO_MIME_TYPE)
1283 #if 0 /* it's dead in GtkFileSystemUnix.c, too */
1284       || ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR)
1285 #endif
1286      )
1287     {
1288 #if 0
1289       const char *mime_type = xdg_mime_get_mime_type_for_file (filename);
1290       gtk_file_info_set_mime_type (info, mime_type);
1291
1292       if ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR &&
1293           (statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) &&
1294           (strcmp (mime_type, XDG_MIME_TYPE_UNKNOWN) == 0 ||
1295            strcmp (mime_type, "application/x-executable") == 0 ||
1296            strcmp (mime_type, "application/x-shellscript") == 0))
1297         gtk_file_info_set_icon_type (info, GTK_FILE_ICON_EXECUTABLE);
1298 #endif
1299     }
1300
1301   if (types & GTK_FILE_INFO_MODIFICATION_TIME)
1302     {
1303       GtkFileTime time = wfad.ftLastWriteTime.dwLowDateTime 
1304                        | ((guint64)wfad.ftLastWriteTime.dwHighDateTime) << 32;
1305       /* 100-nanosecond intervals since January 1, 1601, urgh! */
1306       time /= G_GINT64_CONSTANT (10000000); /* now seconds */
1307       time -= G_GINT64_CONSTANT (134774) * 24 * 3600; /* good old Unix time */
1308       gtk_file_info_set_modification_time (info, time);
1309     }
1310
1311   if (types & GTK_FILE_INFO_SIZE)
1312     {
1313       gint64 size = wfad.nFileSizeLow | ((guint64)wfad.nFileSizeHigh) << 32;
1314       gtk_file_info_set_size (info, size);
1315     }
1316   
1317   return info;
1318 }
1319
1320 static gchar *
1321 filename_from_path (const GtkFilePath *path)
1322 {
1323   return g_strdup (gtk_file_path_get_string (path));
1324 }
1325
1326 static GtkFilePath *
1327 filename_to_path (const char *filename)
1328 {
1329   return gtk_file_path_new_dup (filename);
1330 }
1331
1332 static gboolean
1333 filename_is_root (const char *filename)
1334 {
1335   guint len = strlen(filename);
1336
1337   /* accept both forms */
1338
1339   return (   (len == 2 && filename[1] == ':')
1340           || (len == 3 && filename[1] == ':' && (filename[2] == '\\' || filename[2] == '/')));
1341 }
1342