Add extension point for adding metadata for local files
[platform/upstream/glib.git] / gio / glocalfileenumerator.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 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
16  * Public 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  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include <glib.h>
26 #include <glocalfileenumerator.h>
27 #include <glocalfileinfo.h>
28 #include <glocalfile.h>
29 #include <gioerror.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include "glibintl.h"
33
34 #include "gioalias.h"
35
36 #define CHUNK_SIZE 1000
37
38   /* TODO:
39    *  It would be nice to use the dirent->d_type to check file type without
40    *  needing to stat each files on linux and other systems that support it.
41    *  (question: does that following symlink or not?)
42    */
43
44 #ifdef G_OS_WIN32
45 #define USE_GDIR
46 #endif
47
48 #ifndef USE_GDIR
49
50 #include <sys/types.h>
51 #include <dirent.h>
52 #include <errno.h>
53
54 typedef struct {
55   char *name;
56   long inode;
57 } DirEntry;
58
59 #endif
60
61 struct _GLocalFileEnumerator
62 {
63   GFileEnumerator parent;
64
65   GFileAttributeMatcher *matcher;
66   char *filename;
67   char *attributes;
68   GFileQueryInfoFlags flags;
69
70   gboolean got_parent_info;
71   GLocalParentFileInfo parent_info;
72   
73 #ifdef USE_GDIR
74   GDir *dir;
75 #else
76   DIR *dir;
77   DirEntry *entries;
78   int entries_pos;
79   gboolean at_end;
80 #endif
81   
82   gboolean follow_symlinks;
83 };
84
85 #define g_local_file_enumerator_get_type _g_local_file_enumerator_get_type
86 G_DEFINE_TYPE (GLocalFileEnumerator, g_local_file_enumerator, G_TYPE_FILE_ENUMERATOR);
87
88 static GFileInfo *g_local_file_enumerator_next_file (GFileEnumerator  *enumerator,
89                                                      GCancellable     *cancellable,
90                                                      GError          **error);
91 static gboolean   g_local_file_enumerator_close     (GFileEnumerator  *enumerator,
92                                                      GCancellable     *cancellable,
93                                                      GError          **error);
94
95
96 static void
97 free_entries (GLocalFileEnumerator *local)
98 {
99 #ifndef USE_GDIR
100   int i;
101
102   if (local->entries != NULL)
103     {
104       for (i = 0; local->entries[i].name != NULL; i++)
105         g_free (local->entries[i].name);
106       
107       g_free (local->entries);
108     }
109 #endif
110 }
111
112 static void
113 g_local_file_enumerator_finalize (GObject *object)
114 {
115   GLocalFileEnumerator *local;
116
117   local = G_LOCAL_FILE_ENUMERATOR (object);
118
119   if (local->got_parent_info)
120     _g_local_file_info_free_parent_info (&local->parent_info);
121   g_free (local->filename);
122   g_file_attribute_matcher_unref (local->matcher);
123   if (local->dir)
124     {
125 #ifdef USE_GDIR
126       g_dir_close (local->dir);
127 #else
128       closedir (local->dir);
129 #endif      
130       local->dir = NULL;
131     }
132
133   free_entries (local);
134
135   G_OBJECT_CLASS (g_local_file_enumerator_parent_class)->finalize (object);
136 }
137
138
139 static void
140 g_local_file_enumerator_class_init (GLocalFileEnumeratorClass *klass)
141 {
142   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
143   GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
144   
145   gobject_class->finalize = g_local_file_enumerator_finalize;
146
147   enumerator_class->next_file = g_local_file_enumerator_next_file;
148   enumerator_class->close_fn = g_local_file_enumerator_close;
149 }
150
151 static void
152 g_local_file_enumerator_init (GLocalFileEnumerator *local)
153 {
154 }
155
156 #ifdef USE_GDIR
157 static void
158 convert_file_to_io_error (GError **error,
159                           GError  *file_error)
160 {
161   int new_code;
162
163   if (file_error == NULL)
164     return;
165   
166   new_code = G_IO_ERROR_FAILED;
167   
168   if (file_error->domain == G_FILE_ERROR) 
169     {
170       switch (file_error->code) 
171         {
172         case G_FILE_ERROR_NOENT:
173           new_code = G_IO_ERROR_NOT_FOUND;
174           break;
175         case G_FILE_ERROR_ACCES:
176           new_code = G_IO_ERROR_PERMISSION_DENIED;
177           break;
178         case G_FILE_ERROR_NOTDIR:
179           new_code = G_IO_ERROR_NOT_DIRECTORY;
180           break;
181         case G_FILE_ERROR_MFILE:
182           new_code = G_IO_ERROR_TOO_MANY_OPEN_FILES;
183           break;
184         default:
185           break;
186         }
187     }
188   
189   g_set_error_literal (error, G_IO_ERROR,
190                        new_code,
191                        file_error->message);
192 }
193 #endif
194
195 GFileEnumerator *
196 _g_local_file_enumerator_new (GLocalFile *file,
197                               const char           *attributes,
198                               GFileQueryInfoFlags   flags,
199                               GCancellable         *cancellable,
200                               GError              **error)
201 {
202   GLocalFileEnumerator *local;
203   char *filename = g_file_get_path (G_FILE (file));
204
205 #ifdef USE_GDIR
206   GError *dir_error;
207   GDir *dir;
208   
209   dir_error = NULL;
210   dir = g_dir_open (filename, 0, error != NULL ? &dir_error : NULL);
211   if (dir == NULL) 
212     {
213       if (error != NULL)
214         {
215           convert_file_to_io_error (error, dir_error);
216           g_error_free (dir_error);
217         }
218       g_free (filename);
219       return NULL;
220     }
221 #else
222   DIR *dir;
223   int errsv;
224
225   dir = opendir (filename);
226   if (dir == NULL)
227     {
228       errsv = errno;
229
230       g_set_error_literal (error, G_IO_ERROR,
231                            g_io_error_from_errno (errsv),
232                            g_strerror (errsv));
233       g_free (filename);
234       return NULL;
235     }
236
237 #endif
238   
239   local = g_object_new (G_TYPE_LOCAL_FILE_ENUMERATOR,
240                         "container", file,
241                         NULL);
242
243   local->dir = dir;
244   local->filename = filename;
245   local->matcher = g_file_attribute_matcher_new (attributes);
246   local->flags = flags;
247   
248   return G_FILE_ENUMERATOR (local);
249 }
250
251 #ifndef USE_GDIR
252 static int
253 sort_by_inode (const void *_a, const void *_b)
254 {
255   const DirEntry *a, *b;
256
257   a = _a;
258   b = _b;
259   return a->inode - b->inode;
260 }
261
262 static const char *
263 next_file_helper (GLocalFileEnumerator *local)
264 {
265   struct dirent *entry;
266   const char *filename;
267   int i;
268
269   if (local->at_end)
270     return NULL;
271   
272   if (local->entries == NULL ||
273       (local->entries[local->entries_pos].name == NULL))
274     {
275       if (local->entries == NULL)
276         local->entries = g_new (DirEntry, CHUNK_SIZE + 1);
277       else
278         {
279           /* Restart by clearing old names */
280           for (i = 0; local->entries[i].name != NULL; i++)
281             g_free (local->entries[i].name);
282         }
283       
284       for (i = 0; i < CHUNK_SIZE; i++)
285         {
286           entry = readdir (local->dir);
287           while (entry 
288                  && (0 == strcmp (entry->d_name, ".") ||
289                      0 == strcmp (entry->d_name, "..")))
290             entry = readdir (local->dir);
291
292           if (entry)
293             {
294               local->entries[i].name = g_strdup (entry->d_name);
295               local->entries[i].inode = entry->d_ino;
296             }
297           else
298             break;
299         }
300       local->entries[i].name = NULL;
301       local->entries_pos = 0;
302       
303       qsort (local->entries, i, sizeof (DirEntry), sort_by_inode);
304     }
305
306   filename = local->entries[local->entries_pos++].name;
307   if (filename == NULL)
308     local->at_end = TRUE;
309     
310   return filename;
311 }
312
313 #endif
314
315 static GFileInfo *
316 g_local_file_enumerator_next_file (GFileEnumerator  *enumerator,
317                                    GCancellable     *cancellable,
318                                    GError          **error)
319 {
320   GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);
321   const char *filename;
322   char *path;
323   GFileInfo *info;
324   GError *my_error;
325
326   if (!local->got_parent_info)
327     {
328       _g_local_file_info_get_parent_info (local->filename, local->matcher, &local->parent_info);
329       local->got_parent_info = TRUE;
330     }
331   
332  next_file:
333
334 #ifdef USE_GDIR
335   filename = g_dir_read_name (local->dir);
336 #else
337   filename = next_file_helper (local);
338 #endif
339
340   if (filename == NULL)
341     return NULL;
342
343   my_error = NULL;
344   path = g_build_filename (local->filename, filename, NULL);
345   info = _g_local_file_info_get (filename, path,
346                                  local->matcher,
347                                  local->flags,
348                                  &local->parent_info,
349                                  &my_error); 
350   g_free (path);
351
352   if (info == NULL)
353     {
354       /* Failed to get info */
355       /* If the file does not exist there might have been a race where
356        * the file was removed between the readdir and the stat, so we
357        * ignore the file. */
358       if (my_error->domain == G_IO_ERROR &&
359           my_error->code == G_IO_ERROR_NOT_FOUND)
360         {
361           g_error_free (my_error);
362           goto next_file;
363         }
364       else
365         g_propagate_error (error, my_error);
366     }
367
368   return info;
369 }
370
371 static gboolean
372 g_local_file_enumerator_close (GFileEnumerator  *enumerator,
373                                GCancellable     *cancellable,
374                                GError          **error)
375 {
376   GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);
377
378   if (local->dir)
379     {
380 #ifdef USE_GDIR
381       g_dir_close (local->dir);
382 #else
383       closedir (local->dir);
384 #endif
385       local->dir = NULL;
386     }
387
388   return TRUE;
389 }
390
391