]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemwin32.c
updated added all the new tests
[~andy/gtk] / gtk / gtkfilesystemwin32.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilesystemunix.c: Default implementation of GtkFileSystem for UNIX-like systems
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "gtkfilesystem.h"
22 #include "gtkfilesystemunix.h"
23 #include "gtkintl.h"
24 #include "gtkstock.h"
25
26 #include <errno.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <sys/types.h>
30
31 #ifdef G_OS_WIN32
32 #define WIN32_LEAN_AND_MEAN
33 #include <windows.h>
34 #include <direct.h>
35 #include <io.h>
36 #define mkdir(p,m) _mkdir(p)
37 #else
38 #error "The implementation is win32 only yet."
39 #endif /* G_OS_WIN32 */
40
41 typedef struct _GtkFileSystemUnixClass GtkFileSystemUnixClass;
42
43 #define GTK_FILE_SYSTEM_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
44 #define GTK_IS_FILE_SYSTEM_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_UNIX))
45 #define GTK_FILE_SYSTEM_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
46
47 struct _GtkFileSystemUnixClass
48 {
49   GObjectClass parent_class;
50 };
51
52 struct _GtkFileSystemUnix
53 {
54   GObject parent_instance;
55 };
56
57 #define GTK_TYPE_FILE_FOLDER_UNIX             (gtk_file_folder_unix_get_type ())
58 #define GTK_FILE_FOLDER_UNIX(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnix))
59 #define GTK_IS_FILE_FOLDER_UNIX(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_UNIX))
60 #define GTK_FILE_FOLDER_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
61 #define GTK_IS_FILE_FOLDER_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_UNIX))
62 #define GTK_FILE_FOLDER_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
63
64 typedef struct _GtkFileFolderUnix      GtkFileFolderUnix;
65 typedef struct _GtkFileFolderUnixClass GtkFileFolderUnixClass;
66
67 struct _GtkFileFolderUnixClass
68 {
69   GObjectClass parent_class;
70 };
71
72 struct _GtkFileFolderUnix
73 {
74   GObject parent_instance;
75
76   GtkFileInfoType types;
77   gchar *filename;
78 };
79
80 static GObjectClass *system_parent_class;
81 static GObjectClass *folder_parent_class;
82
83 static void gtk_file_system_unix_class_init   (GtkFileSystemUnixClass *class);
84 static void gtk_file_system_unix_iface_init   (GtkFileSystemIface     *iface);
85 static void gtk_file_system_unix_init         (GtkFileSystemUnix      *impl);
86 static void gtk_file_system_unix_finalize     (GObject                *object);
87
88 static GSList *       gtk_file_system_unix_list_roots    (GtkFileSystem      *file_system);
89 static GtkFileInfo *  gtk_file_system_unix_get_root_info (GtkFileSystem      *file_system,
90                                                           const GtkFilePath  *path,
91                                                           GtkFileInfoType     types,
92                                                           GError            **error);
93 static GtkFileFolder *gtk_file_system_unix_get_folder    (GtkFileSystem      *file_system,
94                                                           const GtkFilePath  *path,
95                                                           GtkFileInfoType     types,
96                                                           GError            **error);
97 static gboolean       gtk_file_system_unix_create_folder (GtkFileSystem      *file_system,
98                                                           const GtkFilePath  *path,
99                                                           GError            **error);
100 static gboolean       gtk_file_system_unix_get_parent    (GtkFileSystem      *file_system,
101                                                           const GtkFilePath  *path,
102                                                           GtkFilePath       **parent,
103                                                           GError            **error);
104 static GtkFilePath *  gtk_file_system_unix_make_path     (GtkFileSystem      *file_system,
105                                                           const GtkFilePath  *base_path,
106                                                           const gchar        *display_name,
107                                                           GError            **error);
108 static gboolean       gtk_file_system_unix_parse         (GtkFileSystem      *file_system,
109                                                           const GtkFilePath  *base_path,
110                                                           const gchar        *str,
111                                                           GtkFilePath       **folder,
112                                                           gchar             **file_part,
113                                                           GError            **error);
114
115 static gchar *      gtk_file_system_unix_path_to_uri      (GtkFileSystem     *file_system,
116                                                            const GtkFilePath *path);
117 static gchar *      gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
118                                                            const GtkFilePath *path);
119 static GtkFilePath *gtk_file_system_unix_uri_to_path      (GtkFileSystem     *file_system,
120                                                            const gchar       *uri);
121 static GtkFilePath *gtk_file_system_unix_filename_to_path (GtkFileSystem     *file_system,
122                                                            const gchar       *filename);
123
124 static gboolean gtk_file_system_unix_add_bookmark    (GtkFileSystem     *file_system,
125                                                       const GtkFilePath *path,
126                                                       GError           **error);
127 static gboolean gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
128                                                       const GtkFilePath *path,
129                                                       GError           **error);
130 static GSList * gtk_file_system_unix_list_bookmarks  (GtkFileSystem *file_system);
131
132 static GType gtk_file_folder_unix_get_type   (void);
133 static void  gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class);
134 static void  gtk_file_folder_unix_iface_init (GtkFileFolderIface     *iface);
135 static void  gtk_file_folder_unix_init       (GtkFileFolderUnix      *impl);
136 static void  gtk_file_folder_unix_finalize   (GObject                *object);
137
138 static GtkFileInfo *gtk_file_folder_unix_get_info      (GtkFileFolder  *folder,
139                                                         const GtkFilePath    *path,
140                                                         GError        **error);
141 static gboolean     gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
142                                                         GSList        **children,
143                                                         GError        **error);
144
145 static gchar *      filename_from_path (const GtkFilePath *path);
146 static GtkFilePath *filename_to_path   (const gchar       *filename);
147
148 static gboolean     filename_is_root  (const char       *filename);
149 static GtkFileInfo *filename_get_info (const gchar      *filename,
150                                        GtkFileInfoType   types,
151                                        GError          **error);
152
153 /*
154  * GtkFileSystemUnix
155  */
156 GType
157 gtk_file_system_unix_get_type (void)
158 {
159   static GType file_system_unix_type = 0;
160
161   if (!file_system_unix_type)
162     {
163       static const GTypeInfo file_system_unix_info =
164       {
165         sizeof (GtkFileSystemUnixClass),
166         NULL,           /* base_init */
167         NULL,           /* base_finalize */
168         (GClassInitFunc) gtk_file_system_unix_class_init,
169         NULL,           /* class_finalize */
170         NULL,           /* class_data */
171         sizeof (GtkFileSystemUnix),
172         0,              /* n_preallocs */
173         (GInstanceInitFunc) gtk_file_system_unix_init,
174       };
175       
176       static const GInterfaceInfo file_system_info =
177       {
178         (GInterfaceInitFunc) gtk_file_system_unix_iface_init, /* interface_init */
179         NULL,                                                 /* interface_finalize */
180         NULL                                                  /* interface_data */
181       };
182
183       file_system_unix_type = g_type_register_static (G_TYPE_OBJECT,
184                                                       "GtkFileSystemUnix",
185                                                       &file_system_unix_info, 0);
186       g_type_add_interface_static (file_system_unix_type,
187                                    GTK_TYPE_FILE_SYSTEM,
188                                    &file_system_info);
189     }
190
191   return file_system_unix_type;
192 }
193
194 /**
195  * gtk_file_system_unix_new:
196  * 
197  * Creates a new #GtkFileSystemUnix object. #GtkFileSystemUnix
198  * implements the #GtkFileSystem interface with direct access to
199  * the filesystem using Unix/Linux API calls
200  * 
201  * Return value: the new #GtkFileSystemUnix object
202  **/
203 GtkFileSystem *
204 gtk_file_system_unix_new (void)
205 {
206   return g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL);
207 }
208
209 static void
210 gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class)
211 {
212   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
213
214   system_parent_class = g_type_class_peek_parent (class);
215   
216   gobject_class->finalize = gtk_file_system_unix_finalize;
217 }
218
219 static void
220 gtk_file_system_unix_iface_init   (GtkFileSystemIface *iface)
221 {
222   iface->list_roots = gtk_file_system_unix_list_roots;
223   iface->get_folder = gtk_file_system_unix_get_folder;
224   iface->get_root_info = gtk_file_system_unix_get_root_info;
225   iface->create_folder = gtk_file_system_unix_create_folder;
226   iface->get_parent = gtk_file_system_unix_get_parent;
227   iface->make_path = gtk_file_system_unix_make_path;
228   iface->parse = gtk_file_system_unix_parse;
229   iface->path_to_uri = gtk_file_system_unix_path_to_uri;
230   iface->path_to_filename = gtk_file_system_unix_path_to_filename;
231   iface->uri_to_path = gtk_file_system_unix_uri_to_path;
232   iface->filename_to_path = gtk_file_system_unix_filename_to_path;
233   iface->add_bookmark = gtk_file_system_unix_add_bookmark;
234   iface->remove_bookmark = gtk_file_system_unix_remove_bookmark;
235   iface->list_bookmarks = gtk_file_system_unix_list_bookmarks;
236 }
237
238 static void
239 gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
240 {
241 }
242
243 static void
244 gtk_file_system_unix_finalize (GObject *object)
245 {
246   system_parent_class->finalize (object);
247 }
248
249 static GSList *
250 gtk_file_system_unix_list_roots (GtkFileSystem *file_system)
251 {
252   gchar   drives[26*4];
253   guint   len;
254   gchar  *p;
255   GSList *list = NULL;
256
257   len = GetLogicalDriveStrings(sizeof(drives), drives);
258
259   if (len < 3)
260     g_warning("No drive strings available!");
261
262   p = drives;
263   while ((len = strlen(p)) != 0)
264     {
265        /* skip floppy */
266        if (p[0] != 'a' && p[0] != 'b')
267          {
268            //FIXME: gtk_fie_path_compare is case sensitive, we are not
269            p[0] = toupper (p[0]);
270            /* needed without the backslash */
271            p[2] = '\0';
272            list = g_slist_append (list, gtk_file_path_new_dup (p));
273          }
274        p += len + 1;
275     }
276   return list;
277 }
278
279 static GtkFileInfo *
280 gtk_file_system_unix_get_root_info (GtkFileSystem    *file_system,
281                                     const GtkFilePath      *path,
282                                     GtkFileInfoType   types,
283                                     GError          **error)
284 {
285   /* needed _with_ the trailing backslash */
286   gchar *filename = g_strconcat(gtk_file_path_get_string (path), "\\", NULL);
287   GtkFileInfo *info;
288   DWORD dt = GetDriveType (filename);
289
290   info = filename_get_info (filename, types, error);
291
292   /* additional info */
293   if (GTK_FILE_INFO_DISPLAY_NAME & types)
294     {
295       gchar display_name[80];
296
297       if (GetVolumeInformation (filename, 
298                                 display_name, sizeof(display_name),
299                                 NULL, /* serial number */
300                                 NULL, /* max. component length */
301                                 NULL, /* fs flags */
302                                 NULL, 0)) /* fs type like FAT, NTFS */
303         {
304           gchar* real_display_name = g_strconcat (display_name, " (", filename, ")", NULL);
305
306           gtk_file_info_set_display_name (info, real_display_name);
307           g_free (real_display_name);
308         }
309       else
310         gtk_file_info_set_display_name (info, filename);
311     }
312
313   if (GTK_FILE_INFO_ICON & types)
314     {
315       switch (dt)
316         {
317         case DRIVE_REMOVABLE :
318           //gtk_file_info_set_icon_type (info, GTK_STOCK_FLOPPY);
319           break;
320         case DRIVE_CDROM :
321           //gtk_file_info_set_icon_type (info, GTK_STOCK_CDROM);
322           break;
323         case DRIVE_REMOTE :
324           //FIXME: need a network stock icon
325         case DRIVE_FIXED :
326           //FIXME: need a hard disk stock icon
327         case DRIVE_RAMDISK :
328           //FIXME: need a ram stock icon
329           //gtk_file_info_set_icon_type (info, GTK_STOCK_OPEN);
330           break;
331         default :
332           g_assert_not_reached ();
333         }
334     }
335   g_free (filename);
336   return info;
337 }
338
339 static GtkFileFolder *
340 gtk_file_system_unix_get_folder (GtkFileSystem     *file_system,
341                                  const GtkFilePath *path,
342                                  GtkFileInfoType    types,
343                                  GError           **error)
344 {
345   GtkFileFolderUnix *folder_unix;
346   gchar *filename;
347
348   filename = filename_from_path (path);
349   g_return_val_if_fail (filename != NULL, NULL);
350
351   folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
352   folder_unix->filename = filename;
353   folder_unix->types = types;
354
355   return GTK_FILE_FOLDER (folder_unix);
356 }
357
358 static gboolean
359 gtk_file_system_unix_create_folder (GtkFileSystem     *file_system,
360                                     const GtkFilePath *path,
361                                     GError           **error)
362 {
363   gchar *filename;
364   gboolean result;
365
366   filename = filename_from_path (path);
367   g_return_val_if_fail (filename != NULL, FALSE);
368   
369   result = mkdir (filename, 0777) != 0;
370   
371   if (!result)
372     {
373       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
374       g_set_error (error,
375                    GTK_FILE_SYSTEM_ERROR,
376                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
377                    _("error creating directory '%s': %s"),
378                    filename_utf8 ? filename_utf8 : "???",
379                    g_strerror (errno));
380       g_free (filename_utf8);
381     }
382   
383   g_free (filename);
384   
385   return result;
386 }
387
388 static gboolean
389 gtk_file_system_unix_get_parent (GtkFileSystem     *file_system,
390                                  const GtkFilePath *path,
391                                  GtkFilePath      **parent,
392                                  GError           **error)
393 {
394   gchar *filename = filename_from_path (path);
395   g_return_val_if_fail (filename != NULL, FALSE);
396
397   if (filename_is_root (filename))
398     {
399       *parent = NULL;
400     }
401   else
402     {
403       gchar *parent_filename = g_path_get_dirname (filename);
404       *parent = filename_to_path (parent_filename);
405       g_free (parent_filename);
406     }
407
408   g_free (filename);
409
410   return TRUE;
411 }
412
413 static GtkFilePath *
414 gtk_file_system_unix_make_path (GtkFileSystem    *file_system,
415                                const GtkFilePath *base_path,
416                                const gchar       *display_name,
417                                GError           **error)
418 {
419   gchar *base_filename;
420   gchar *filename;
421   gchar *full_filename;
422   GError *tmp_error = NULL;
423   GtkFilePath *result;
424   
425   base_filename = filename_from_path (base_path);
426   g_return_val_if_fail (base_filename != NULL, NULL);
427
428   filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
429   if (!filename)
430     {
431       g_set_error (error,
432                    GTK_FILE_SYSTEM_ERROR,
433                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
434                    "%s",
435                    tmp_error->message);
436       
437       g_error_free (tmp_error);
438       g_free (base_filename);
439
440       return FALSE;
441     }
442     
443   full_filename = g_build_filename (base_filename, filename, NULL);
444   result = filename_to_path (full_filename);
445   g_free (base_filename);
446   g_free (filename);
447   g_free (full_filename);
448   
449   return result;
450 }
451
452 /* If this was a publically exported function, it should return
453  * a dup'ed result, but we make it modify-in-place for efficiency
454  * here, and because it works for us.
455  */
456 static void
457 canonicalize_filename (gchar *filename)
458 {
459   gchar *p, *q;
460   gboolean last_was_slash = FALSE;
461
462   p = filename;
463   q = filename;
464
465   while (*p)
466     {
467       if (*p == G_DIR_SEPARATOR)
468         {
469           if (!last_was_slash)
470             *q++ = G_DIR_SEPARATOR;
471
472           last_was_slash = TRUE;
473         }
474       else
475         {
476           if (last_was_slash && *p == '.')
477             {
478               if (*(p + 1) == G_DIR_SEPARATOR ||
479                   *(p + 1) == '\0')
480                 {
481                   if (*(p + 1) == '\0')
482                     break;
483                   
484                   p += 1;
485                 }
486               else if (*(p + 1) == '.' &&
487                        (*(p + 2) == G_DIR_SEPARATOR ||
488                         *(p + 2) == '\0'))
489                 {
490                   if (q > filename + 1)
491                     {
492                       q--;
493                       while (q > filename + 1 &&
494                              *(q - 1) != G_DIR_SEPARATOR)
495                         q--;
496                     }
497
498                   if (*(p + 2) == '\0')
499                     break;
500                   
501                   p += 2;
502                 }
503               else
504                 {
505                   *q++ = *p;
506                   last_was_slash = FALSE;
507                 }
508             }
509           else
510             {
511               *q++ = *p;
512               last_was_slash = FALSE;
513             }
514         }
515
516       p++;
517     }
518
519   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
520     q--;
521
522   *q = '\0';
523 }
524
525 static gboolean
526 gtk_file_system_unix_parse (GtkFileSystem     *file_system,
527                             const GtkFilePath *base_path,
528                             const gchar       *str,
529                             GtkFilePath      **folder,
530                             gchar            **file_part,
531                             GError           **error)
532 {
533   char *base_filename;
534   gchar *last_slash;
535   gboolean result = FALSE;
536
537   base_filename = filename_from_path (base_path);
538   g_return_val_if_fail (base_filename != NULL, FALSE);
539   
540   last_slash = strrchr (str, G_DIR_SEPARATOR);
541   if (!last_slash)
542     {
543       *folder = gtk_file_path_copy (base_path);
544       *file_part = g_strdup (str);
545       result = TRUE;
546     }
547   else
548     {
549       gchar *folder_part;
550       gchar *folder_path;
551       GError *tmp_error = NULL;
552
553       if (last_slash == str)
554         folder_part = g_strdup ("/");
555       else
556         folder_part = g_filename_from_utf8 (str, last_slash - str,
557                                             NULL, NULL, &tmp_error);
558
559       if (!folder_part)
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           g_error_free (tmp_error);
567         }
568       else
569         {
570           if (folder_part[1] == ':')
571             folder_path = folder_part;
572           else
573             {
574               folder_path = g_build_filename (base_filename, folder_part, NULL);
575               g_free (folder_part);
576             }
577
578           canonicalize_filename (folder_path);
579           
580           *folder = filename_to_path (folder_path);
581           *file_part = g_strdup (last_slash + 1);
582
583           g_free (folder_path);
584
585           result = TRUE;
586         }
587     }
588
589   g_free (base_filename);
590   
591   return result;
592 }
593
594 static gchar *
595 gtk_file_system_unix_path_to_uri (GtkFileSystem     *file_system,
596                                   const GtkFilePath *path)
597 {
598   return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
599 }
600
601 static gchar *
602 gtk_file_system_unix_path_to_filename (GtkFileSystem     *file_system,
603                                        const GtkFilePath *path)
604 {
605   return g_strdup (gtk_file_path_get_string (path));
606 }
607
608 static GtkFilePath *
609 gtk_file_system_unix_uri_to_path (GtkFileSystem     *file_system,
610                                   const gchar       *uri)
611 {
612   gchar *filename = g_filename_from_uri (uri, NULL, NULL);
613   if (filename)
614     return gtk_file_path_new_steal (filename);
615   else
616     return NULL;
617 }
618
619 static GtkFilePath *
620 gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
621                                        const gchar   *filename)
622 {
623   return gtk_file_path_new_dup (filename);
624 }
625
626 static gboolean
627 bookmarks_serialize (GSList  **bookmarks,
628                      gchar    *uri,
629                      gboolean  add,
630                      GError  **error)
631 {
632   gchar   *filename;
633   gboolean ok = TRUE;
634   GSList  *list = *bookmarks;
635
636   filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
637
638   if (filename)
639     {
640       gchar *contents = NULL;
641       gsize  len = 0;
642       GList *entry;
643       FILE  *f;   
644        
645       if (g_file_test (filename, G_FILE_TEST_EXISTS))
646         {
647           if (g_file_get_contents (filename, &contents, &len, error))
648             {
649               gchar **lines = g_strsplit (contents, "\n", -1);
650               gint    i;
651
652               for (i = 0; lines[i] != NULL; i++)
653                 {
654                   if (lines[i][0] && !g_slist_find_custom (list, lines[i], strcmp))
655                     list = g_slist_append (list, g_strdup (lines[i]));
656                 }
657               g_strfreev (lines);
658             }
659           else
660             ok = FALSE;
661         }
662       if (ok && (f = fopen (filename, "wb")) != NULL)
663         {
664           for (entry = list; entry != NULL; entry = entry->next)
665             {
666               gchar *line = entry->data;
667
668               if (strcmp (line, uri) != 0)
669                 {
670                   fputs (line, f);
671                   fputs ("\n", f);
672                 }
673             }
674           if (add)
675             {
676               fputs (uri, f);
677               fputs ("\n", f);
678             }
679           fclose (f);
680         }
681       else if (ok && error)
682         {
683           g_set_error (error,
684                        GTK_FILE_SYSTEM_ERROR,
685                        GTK_FILE_SYSTEM_ERROR_FAILED,
686                        _("Bookmark saving failed (%s)"),
687                        g_strerror (errno));
688         }
689     }
690   *bookmarks = list;
691   return ok;
692 }
693
694 static GSList *_bookmarks = NULL;
695
696 static gboolean
697 gtk_file_system_unix_add_bookmark (GtkFileSystem     *file_system,
698                                    const GtkFilePath *path,
699                                    GError           **error)
700 {
701   gchar *uri = gtk_file_system_unix_path_to_uri (file_system, path);
702   gboolean ret = bookmarks_serialize (&_bookmarks, uri, TRUE, error);
703   g_free (uri);
704   return ret;
705                                
706 }
707
708 static gboolean
709 gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
710                                       const GtkFilePath *path,
711                                       GError           **error)
712 {
713   gchar *uri = gtk_file_system_unix_path_to_uri (file_system, path);
714   gboolean ret = bookmarks_serialize (&_bookmarks, uri, FALSE, error);
715   g_free (uri);
716   return ret;
717 }
718
719 static GSList *
720 gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
721 {
722   GSList *list = NULL;
723   GSList *entry;
724
725   if (bookmarks_serialize (&_bookmarks, "", FALSE, NULL))
726     {
727       GSList *entry;
728       
729       for (entry = _bookmarks; entry != NULL; entry = entry->next)
730         {
731           GtkFilePath *path = gtk_file_system_unix_uri_to_path (
732                                 file_system, (gchar *)entry->data);
733
734           list = g_slist_append (list, path);
735         }
736     }
737
738   return list;
739 }
740
741 /*
742  * GtkFileFolderUnix
743  */
744 static GType
745 gtk_file_folder_unix_get_type (void)
746 {
747   static GType file_folder_unix_type = 0;
748
749   if (!file_folder_unix_type)
750     {
751       static const GTypeInfo file_folder_unix_info =
752       {
753         sizeof (GtkFileFolderUnixClass),
754         NULL,           /* base_init */
755         NULL,           /* base_finalize */
756         (GClassInitFunc) gtk_file_folder_unix_class_init,
757         NULL,           /* class_finalize */
758         NULL,           /* class_data */
759         sizeof (GtkFileFolderUnix),
760         0,              /* n_preallocs */
761         (GInstanceInitFunc) gtk_file_folder_unix_init,
762       };
763       
764       static const GInterfaceInfo file_folder_info =
765       {
766         (GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
767         NULL,                                                 /* interface_finalize */
768         NULL                                                  /* interface_data */
769       };
770
771       file_folder_unix_type = g_type_register_static (G_TYPE_OBJECT,
772                                                       "GtkFileFolderUnix",
773                                                       &file_folder_unix_info, 0);
774       g_type_add_interface_static (file_folder_unix_type,
775                                    GTK_TYPE_FILE_FOLDER,
776                                    &file_folder_info);
777     }
778
779   return file_folder_unix_type;
780 }
781
782 static void
783 gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
784 {
785   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
786
787   folder_parent_class = g_type_class_peek_parent (class);
788
789   gobject_class->finalize = gtk_file_folder_unix_finalize;
790 }
791
792 static void
793 gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
794 {
795   iface->get_info = gtk_file_folder_unix_get_info;
796   iface->list_children = gtk_file_folder_unix_list_children;
797 }
798
799 static void
800 gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
801 {
802 }
803
804 static void
805 gtk_file_folder_unix_finalize (GObject *object)
806 {
807   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
808
809   g_free (folder_unix->filename);
810   
811   folder_parent_class->finalize (object);
812 }
813
814 static GtkFileInfo *
815 gtk_file_folder_unix_get_info (GtkFileFolder  *folder,
816                                const GtkFilePath    *path,
817                                GError        **error)
818 {
819   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
820   GtkFileInfo *info;
821   gchar *dirname;
822   gchar *filename;
823   
824   filename = filename_from_path (path);
825   g_return_val_if_fail (filename != NULL, NULL);
826
827 #if 0
828   dirname = g_path_get_dirname (filename);
829   g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
830   g_free (dirname);
831 #endif
832
833   info = filename_get_info (filename, folder_unix->types, error);
834
835   g_free (filename);
836
837   return info;
838 }
839
840 static gboolean
841 gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
842                                     GSList        **children,
843                                     GError        **error)
844 {
845   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
846   GError *tmp_error = NULL;
847   GDir *dir;
848
849   *children = NULL;
850
851   dir = g_dir_open (folder_unix->filename, 0, &tmp_error);
852   if (!dir)
853     {
854       g_set_error (error,
855                    GTK_FILE_SYSTEM_ERROR,
856                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
857                    "%s",
858                    tmp_error->message);
859       
860       g_error_free (tmp_error);
861
862       return FALSE;
863     }
864
865   while (TRUE)
866     {
867       const gchar *filename = g_dir_read_name (dir);
868       gchar *fullname;
869
870       if (!filename)
871         break;
872
873       fullname = g_build_filename (folder_unix->filename, filename, NULL);
874       *children = g_slist_prepend (*children, filename_to_path (fullname));
875       g_free (fullname);
876     }
877
878   g_dir_close (dir);
879
880   *children = g_slist_reverse (*children);
881   
882   return TRUE;
883 }
884
885 static GtkFileInfo *
886 filename_get_info (const gchar     *filename,
887                    GtkFileInfoType  types,
888                    GError         **error)
889 {
890   GtkFileInfo *info;
891   GtkFileIconType icon_type = GTK_FILE_ICON_REGULAR;
892   WIN32_FILE_ATTRIBUTE_DATA wfad;
893
894   if (!GetFileAttributesEx (filename, GetFileExInfoStandard, &wfad))
895     {
896       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
897       g_set_error (error,
898                    GTK_FILE_SYSTEM_ERROR,
899                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
900                    _("error getting information for '%s': %s"),
901                    filename_utf8 ? filename_utf8 : "???",
902                    g_win32_error_message (GetLastError ()));
903       g_free (filename_utf8);
904       
905       return NULL;
906     }
907
908   info = gtk_file_info_new ();
909
910   if (filename_is_root (filename))
911     {
912       if (types & GTK_FILE_INFO_DISPLAY_NAME)
913         gtk_file_info_set_display_name (info, filename);
914       
915       if (types & GTK_FILE_INFO_IS_HIDDEN)
916         gtk_file_info_set_is_hidden (info, FALSE);
917     }
918   else
919     {
920       gchar *basename = g_path_get_basename (filename);
921   
922       if (types & GTK_FILE_INFO_DISPLAY_NAME)
923         {
924           gchar *display_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
925           if (!display_name)
926             display_name = g_strescape (basename, NULL);
927           
928           gtk_file_info_set_display_name (info, display_name);
929           
930           g_free (display_name);
931         }
932       
933       if (types & GTK_FILE_INFO_IS_HIDDEN)
934         {
935             /* unix convention ... */
936             gboolean is_hidden = basename[0] == '.';
937             /* ... _and_ windoze attribute */
938             is_hidden = is_hidden || !!(wfad.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
939           gtk_file_info_set_is_hidden (info, is_hidden);
940         }
941
942       g_free (basename);
943     }
944
945   if (types & GTK_FILE_INFO_IS_FOLDER)
946     {
947       gtk_file_info_set_is_folder (info, !!(wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
948    }
949
950   if (types & GTK_FILE_INFO_ICON)
951     {
952       if (wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
953         icon_type = GTK_FILE_ICON_DIRECTORY;
954
955       gtk_file_info_set_icon_type (info, icon_type);
956     }
957
958   if ((types & GTK_FILE_INFO_MIME_TYPE) ||
959       ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR))
960     {
961 #if 0
962       const char *mime_type = xdg_mime_get_mime_type_for_file (filename);
963       gtk_file_info_set_mime_type (info, mime_type);
964
965       if ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR &&
966           (statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) &&
967           (strcmp (mime_type, XDG_MIME_TYPE_UNKNOWN) == 0 ||
968            strcmp (mime_type, "application/x-executable") == 0 ||
969            strcmp (mime_type, "application/x-shellscript") == 0))
970         gtk_file_info_set_icon_type (info, GTK_FILE_ICON_EXECUTABLE);
971 #endif
972     }
973
974   if (types & GTK_FILE_INFO_MODIFICATION_TIME)
975     {
976       GtkFileTime time = wfad.ftLastWriteTime.dwLowDateTime 
977                        | ((guint64)wfad.ftLastWriteTime.dwHighDateTime) << 32;
978       /* 100-nanosecond intervals since January 1, 1601, urgh! */
979       time /= 10000000I64; /* now seconds */
980       time -= 134774I64 * 24 * 3600; /* good old unix time */
981       gtk_file_info_set_modification_time (info, time);
982     }
983
984   if (types & GTK_FILE_INFO_SIZE)
985     {
986       gint64 size = wfad.nFileSizeLow | ((guint64)wfad.nFileSizeHigh) << 32;
987       gtk_file_info_set_size (info, size);
988     }
989   
990   return info;
991 }
992
993 static gchar *
994 filename_from_path (const GtkFilePath *path)
995 {
996   return g_strdup (gtk_file_path_get_string (path));
997 }
998
999 static GtkFilePath *
1000 filename_to_path (const char *filename)
1001 {
1002   return gtk_file_path_new_dup (filename);
1003 }
1004
1005 static gboolean
1006 filename_is_root (const char *filename)
1007 {
1008   guint len = strlen(filename);
1009
1010   /* accept both forms */
1011
1012   return (   (len == 2 && filename[1] == ':')
1013           || (len == 3 && filename[1] == ':' && filename[2] == '\\'));
1014 }
1015