]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemunix.c
Add some documentation comments, fix some missing statics
[~andy/gtk] / gtk / gtkfilesystemunix.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilesystemunix.c: Default implementation of GtkFileSystem for UNIX-like systems
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "gtkfilesystem.h"
22 #include "gtkfilesystemunix.h"
23
24 #include <errno.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29
30 typedef struct _GtkFileSystemUnixClass GtkFileSystemUnixClass;
31
32 #define GTK_FILE_SYSTEM_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
33 #define GTK_IS_FILE_SYSTEM_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_UNIX))
34 #define GTK_FILE_SYSTEM_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
35
36 struct _GtkFileSystemUnixClass
37 {
38   GObjectClass parent_class;
39 };
40
41 struct _GtkFileSystemUnix
42 {
43   GObject parent_instance;
44 };
45
46 #define GTK_TYPE_FILE_FOLDER_UNIX             (gtk_file_folder_unix_get_type ())
47 #define GTK_FILE_FOLDER_UNIX(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnix))
48 #define GTK_IS_FILE_FOLDER_UNIX(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_UNIX))
49 #define GTK_FILE_FOLDER_UNIX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
50 #define GTK_IS_FILE_FOLDER_UNIX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_UNIX))
51 #define GTK_FILE_FOLDER_UNIX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
52
53 typedef struct _GtkFileFolderUnix      GtkFileFolderUnix;
54 typedef struct _GtkFileFolderUnixClass GtkFileFolderUnixClass;
55
56 struct _GtkFileFolderUnixClass
57 {
58   GObjectClass parent_class;
59 };
60
61 struct _GtkFileFolderUnix
62 {
63   GObject parent_instance;
64
65   GtkFileInfoType types;
66   gchar *filename;
67 };
68
69 static void gtk_file_system_unix_class_init   (GtkFileSystemUnixClass *class);
70 static void gtk_file_system_unix_iface_init   (GtkFileSystemIface     *iface);
71 static void gtk_file_system_unix_init         (GtkFileSystemUnix      *impl);
72 static void gtk_file_system_unix_finalize     (GObject                *object);
73
74 static GSList *       gtk_file_system_unix_list_roots    (GtkFileSystem    *file_system);
75 static GtkFileInfo *  gtk_file_system_unix_get_root_info (GtkFileSystem    *file_system,
76                                                           const gchar      *uri,
77                                                           GtkFileInfoType   types,
78                                                           GError          **error);
79 static GtkFileFolder *gtk_file_system_unix_get_folder    (GtkFileSystem    *file_system,
80                                                           const gchar      *uri,
81                                                           GtkFileInfoType   types,
82                                                           GError          **error);
83 static gboolean       gtk_file_system_unix_create_folder (GtkFileSystem    *file_system,
84                                                           const gchar      *uri,
85                                                           GError          **error);
86 static gboolean       gtk_file_system_unix_get_parent    (GtkFileSystem    *file_system,
87                                                           const gchar      *uri,
88                                                           gchar           **parent,
89                                                           GError          **error);
90 static gchar *        gtk_file_system_unix_make_uri      (GtkFileSystem    *file_system,
91                                                           const gchar      *base_uri,
92                                                           const gchar      *display_name,
93                                                           GError          **error);
94
95 static GType gtk_file_folder_unix_get_type   (void);
96 static void  gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class);
97 static void  gtk_file_folder_unix_iface_init (GtkFileFolderIface     *iface);
98 static void  gtk_file_folder_unix_init       (GtkFileFolderUnix      *impl);
99 static void  gtk_file_folder_unix_finalize   (GObject                *object);
100
101 static GtkFileInfo *gtk_file_folder_unix_get_info      (GtkFileFolder  *folder,
102                                                         const gchar    *uri,
103                                                         GError        **error);
104 static gboolean     gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
105                                                         GSList        **children,
106                                                         GError        **error);
107
108 static gchar *      filename_from_uri (const gchar      *filename,
109                                        GError          **error);
110 static gchar *      filename_to_uri   (const gchar      *filename);
111 static gboolean     filename_is_root  (const char       *filename);
112 static GtkFileInfo *filename_get_info (const gchar      *filename,
113                                        GtkFileInfoType   types,
114                                        GError          **error);
115
116 /*
117  * GtkFileSystemUnix
118  */
119 GType
120 _gtk_file_system_unix_get_type (void)
121 {
122   static GType file_system_unix_type = 0;
123
124   if (!file_system_unix_type)
125     {
126       static const GTypeInfo file_system_unix_info =
127       {
128         sizeof (GtkFileSystemUnixClass),
129         NULL,           /* base_init */
130         NULL,           /* base_finalize */
131         (GClassInitFunc) gtk_file_system_unix_class_init,
132         NULL,           /* class_finalize */
133         NULL,           /* class_data */
134         sizeof (GtkFileSystemUnix),
135         0,              /* n_preallocs */
136         (GInstanceInitFunc) gtk_file_system_unix_init,
137       };
138       
139       static const GInterfaceInfo file_system_info =
140       {
141         (GInterfaceInitFunc) gtk_file_system_unix_iface_init, /* interface_init */
142         NULL,                                                 /* interface_finalize */
143         NULL                                                  /* interface_data */
144       };
145
146       file_system_unix_type = g_type_register_static (G_TYPE_OBJECT,
147                                                       "GtkFileSystemUnix",
148                                                       &file_system_unix_info, 0);
149       g_type_add_interface_static (file_system_unix_type,
150                                    GTK_TYPE_FILE_SYSTEM,
151                                    &file_system_info);
152     }
153
154   return file_system_unix_type;
155 }
156
157 /**
158  * _gtk_file_system_unix_new:
159  * 
160  * Creates a new #GtkFileSystemUnix object. #GtkFileSystemUnix
161  * implements the #GtkFileSystem interface with direct access to
162  * the filesystem using Unix/Linux API calls
163  * 
164  * Return value: the new #GtkFileSystemUnix object
165  **/
166 GtkFileSystem *
167 _gtk_file_system_unix_new (void)
168 {
169   return g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL);
170 }
171
172 static void
173 gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class)
174 {
175   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
176   
177   gobject_class->finalize = gtk_file_system_unix_finalize;
178 }
179
180 static void
181 gtk_file_system_unix_iface_init   (GtkFileSystemIface *iface)
182 {
183   iface->list_roots = gtk_file_system_unix_list_roots;
184   iface->get_folder = gtk_file_system_unix_get_folder;
185   iface->get_root_info = gtk_file_system_unix_get_root_info;
186   iface->create_folder = gtk_file_system_unix_create_folder;
187   iface->get_parent = gtk_file_system_unix_get_parent;
188   iface->make_uri = gtk_file_system_unix_make_uri;
189 }
190
191 static void
192 gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
193 {
194 }
195
196 static void
197 gtk_file_system_unix_finalize (GObject *object)
198 {
199 }
200
201 static GSList *
202 gtk_file_system_unix_list_roots (GtkFileSystem *file_system)
203 {
204   return g_slist_append (NULL, g_strdup ("file:///"));
205 }
206
207 static GtkFileInfo *
208 gtk_file_system_unix_get_root_info (GtkFileSystem    *file_system,
209                                     const gchar      *uri,
210                                     GtkFileInfoType   types,
211                                     GError          **error)
212 {
213   g_return_val_if_fail (strcmp (uri, "file:///") == 0, NULL);
214
215   return filename_get_info ("/", types, error);
216 }
217
218 static GtkFileFolder *
219 gtk_file_system_unix_get_folder (GtkFileSystem    *file_system,
220                                  const gchar      *uri,
221                                  GtkFileInfoType   types,
222                                  GError          **error)
223 {
224   GtkFileFolderUnix *folder_unix;
225   gchar *filename;
226
227   filename = filename_from_uri (uri, error);
228   if (!filename)
229     return NULL;
230
231   folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
232   folder_unix->filename = filename;
233   folder_unix->types = types;
234
235   return GTK_FILE_FOLDER (folder_unix);
236 }
237
238 static gboolean
239 gtk_file_system_unix_create_folder (GtkFileSystem    *file_system,
240                                     const gchar      *uri,
241                                     GError          **error)
242 {
243   gchar *filename;
244   gboolean result;
245
246   filename = filename_from_uri (uri, error);
247   if (!filename)
248     return FALSE;
249
250   result = mkdir (filename, 0777) != 0;
251   
252   if (!result)
253     {
254       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
255       g_set_error (error,
256                    GTK_FILE_SYSTEM_ERROR,
257                    GTK_FILE_SYSTEM_ERROR_NONEXISTANT,
258                    "error creating directory '%s': %s",
259                    filename_utf8 ? filename_utf8 : "???",
260                    g_strerror (errno));
261       g_free (filename_utf8);
262     }
263   
264   g_free (filename);
265   
266   return result;
267 }
268
269 static gboolean
270 gtk_file_system_unix_get_parent (GtkFileSystem    *file_system,
271                                  const gchar      *uri,
272                                  gchar           **parent,
273                                  GError          **error)
274 {
275   gchar *filename = filename_from_uri (uri, error);
276   
277   if (!filename)
278     return FALSE;
279
280   if (filename_is_root (filename))
281     {
282       *parent = NULL;
283     }
284   else
285     {
286       gchar *parent_filename = g_path_get_dirname (filename);
287       *parent = filename_to_uri (parent_filename);
288       g_free (parent_filename);
289     }
290
291   g_free (filename);
292
293   return TRUE;
294 }
295
296 static gchar *
297 gtk_file_system_unix_make_uri (GtkFileSystem *file_system,
298                                const gchar   *base_uri,
299                                const gchar   *display_name,
300                                GError       **error)
301 {
302   gchar *base_filename;
303   gchar *filename;
304   gchar *full_filename;
305   GError *tmp_error = NULL;
306   gchar *result;
307   
308   base_filename = filename_from_uri (base_uri, error);
309   if (!base_filename)
310     return FALSE;
311
312   filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
313   if (!filename)
314     {
315       g_set_error (error,
316                    GTK_FILE_SYSTEM_ERROR,
317                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
318                    "%s",
319                    tmp_error->message);
320       
321       g_error_free (tmp_error);
322       g_free (base_filename);
323
324       return FALSE;
325     }
326     
327   full_filename = g_build_filename (base_filename, filename, NULL);
328   result = filename_to_uri (full_filename);
329   g_free (base_filename);
330   g_free (filename);
331   g_free (full_filename);
332   
333   return result;
334 }
335
336 /*
337  * GtkFileFolderUnix
338  */
339 static GType
340 gtk_file_folder_unix_get_type (void)
341 {
342   static GType file_folder_unix_type = 0;
343
344   if (!file_folder_unix_type)
345     {
346       static const GTypeInfo file_folder_unix_info =
347       {
348         sizeof (GtkFileFolderUnixClass),
349         NULL,           /* base_init */
350         NULL,           /* base_finalize */
351         (GClassInitFunc) gtk_file_folder_unix_class_init,
352         NULL,           /* class_finalize */
353         NULL,           /* class_data */
354         sizeof (GtkFileFolderUnix),
355         0,              /* n_preallocs */
356         (GInstanceInitFunc) gtk_file_folder_unix_init,
357       };
358       
359       static const GInterfaceInfo file_folder_info =
360       {
361         (GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
362         NULL,                                                 /* interface_finalize */
363         NULL                                                  /* interface_data */
364       };
365
366       file_folder_unix_type = g_type_register_static (G_TYPE_OBJECT,
367                                                       "GtkFileFolderUnix",
368                                                       &file_folder_unix_info, 0);
369       g_type_add_interface_static (file_folder_unix_type,
370                                    GTK_TYPE_FILE_FOLDER,
371                                    &file_folder_info);
372     }
373
374   return file_folder_unix_type;
375 }
376
377 static void
378 gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
379 {
380   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
381
382   gobject_class->finalize = gtk_file_folder_unix_finalize;
383 }
384
385 static void
386 gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
387 {
388   iface->get_info = gtk_file_folder_unix_get_info;
389   iface->list_children = gtk_file_folder_unix_list_children;
390 }
391
392 static void
393 gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
394 {
395 }
396
397 static void
398 gtk_file_folder_unix_finalize (GObject *object)
399 {
400   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
401
402   g_free (folder_unix->filename);
403 }
404
405 static GtkFileInfo *
406 gtk_file_folder_unix_get_info (GtkFileFolder  *folder,
407                                const gchar    *uri,
408                                GError        **error)
409 {
410   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
411   GtkFileInfo *info;
412   gchar *dirname;
413   gchar *filename;
414   
415   filename = filename_from_uri (uri, error);
416   if (!filename)
417     return NULL;
418
419   dirname = g_path_get_dirname (filename);
420   g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
421   g_free (dirname);
422
423   info = filename_get_info (filename, folder_unix->types, error);
424
425   g_free (filename);
426
427   return info;
428 }
429
430 static gboolean
431 gtk_file_folder_unix_list_children (GtkFileFolder  *folder,
432                                     GSList        **children,
433                                     GError        **error)
434 {
435   GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
436   GError *tmp_error = NULL;
437   GDir *dir;
438
439   *children = NULL;
440
441   dir = g_dir_open (folder_unix->filename, 0, &tmp_error);
442   if (!dir)
443     {
444       g_set_error (error,
445                    GTK_FILE_SYSTEM_ERROR,
446                    GTK_FILE_SYSTEM_ERROR_NONEXISTANT,
447                    "%s",
448                    tmp_error->message);
449       
450       g_error_free (tmp_error);
451
452       return FALSE;
453     }
454
455   while (TRUE)
456     {
457       const gchar *filename = g_dir_read_name (dir);
458       gchar *fullname;
459
460       if (!filename)
461         break;
462
463       fullname = g_build_filename (folder_unix->filename, filename, NULL);
464       *children = g_slist_prepend (*children, filename_to_uri (fullname));
465       g_free (fullname);
466     }
467
468   g_dir_close (dir);
469
470   *children = g_slist_reverse (*children);
471   
472   return TRUE;
473 }
474
475 static GtkFileInfo *
476 filename_get_info (const gchar     *filename,
477                    GtkFileInfoType  types,
478                    GError         **error)
479 {
480   GtkFileInfo *info;
481   struct stat statbuf;
482   
483   /* If stat fails, try to fall back to lstat to catch broken links
484    */
485   if (stat (filename, &statbuf) != 0 &&
486       lstat (filename, &statbuf) != 0)
487     {
488       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
489       g_set_error (error,
490                    GTK_FILE_SYSTEM_ERROR,
491                    GTK_FILE_SYSTEM_ERROR_NONEXISTANT,
492                    "error getting information for '%s': %s",
493                    filename_utf8 ? filename_utf8 : "???",
494                    g_strerror (errno));
495       g_free (filename_utf8);
496       
497       return NULL;
498     }
499
500   info = gtk_file_info_new ();
501
502   if (filename_is_root (filename))
503     {
504       if (types & GTK_FILE_INFO_DISPLAY_NAME)
505         gtk_file_info_set_display_name (info, "/");
506       
507       if (types & GTK_FILE_INFO_IS_HIDDEN)
508         gtk_file_info_set_is_hidden (info, FALSE);
509     }
510   else
511     {
512       gchar *basename = g_path_get_basename (filename);
513   
514       if (types & GTK_FILE_INFO_DISPLAY_NAME)
515         {
516           gchar *display_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
517           if (!display_name)
518             display_name = g_strescape (basename, NULL);
519           
520           gtk_file_info_set_display_name (info, display_name);
521           
522           g_free (display_name);
523         }
524       
525       if (types & GTK_FILE_INFO_IS_HIDDEN)
526         {
527           gtk_file_info_set_is_hidden (info, basename[0] == '.');
528         }
529
530       g_free (basename);
531     }
532
533   if (types & GTK_FILE_INFO_IS_FOLDER)
534     {
535       gtk_file_info_set_is_folder (info, S_ISDIR (statbuf.st_mode));
536    }
537
538   if (types & GTK_FILE_INFO_MIME_TYPE)
539     {
540       gtk_file_info_set_mime_type (info, "application/octet-stream");
541     }
542
543   if (types & GTK_FILE_INFO_MODIFICATION_TIME)
544     {
545       gtk_file_info_set_modification_time (info, statbuf.st_mtime);
546     }
547
548   if (types & GTK_FILE_INFO_SIZE)
549     {
550       gtk_file_info_set_size (info, (gint64)512 * (gint16)statbuf.st_blocks);
551     }
552   
553   if (types & GTK_FILE_INFO_ICON)
554     {
555       /* NOT YET IMPLEMENTED */
556     }
557
558   return info;
559 }
560
561 static gchar *
562 filename_from_uri (const char *uri,
563                    GError    **error)
564 {
565   GError *tmp_error = NULL;
566   gchar *result;
567
568   result = g_filename_from_uri (uri, NULL, &tmp_error);
569   if (!result)
570     {
571       g_set_error (error,
572                    GTK_FILE_SYSTEM_ERROR,
573                    GTK_FILE_SYSTEM_ERROR_INVALID_URI,
574                    "%s",
575                    tmp_error->message);
576       
577       g_error_free (tmp_error);
578     }
579
580   return result;
581 }
582
583 static gchar *
584 filename_to_uri (const char *filename)
585 {
586   gchar *result;
587
588   result = g_filename_to_uri (filename, NULL, NULL);
589   if (!result)
590     g_warning ("GtkFileSystemUnix: Handling of non-UTF-8-representable-filenames needs to be fixed");
591
592   return result;
593 }
594
595 static gboolean
596 filename_is_root (const char *filename)
597 {
598   const gchar *after_root;
599   
600   after_root = g_path_skip_root (filename);
601   
602   return (*after_root == '\0');
603 }