localfileenumerator: Take the type from the readdir() call
[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
35 #define CHUNK_SIZE 1000
36
37 #ifdef G_OS_WIN32
38 #define USE_GDIR
39 #endif
40
41 #ifndef USE_GDIR
42
43 #include <sys/types.h>
44 #include <dirent.h>
45 #include <errno.h>
46
47 typedef struct {
48   char *name;
49   long inode;
50   GFileType type;
51 } DirEntry;
52
53 #endif
54
55 struct _GLocalFileEnumerator
56 {
57   GFileEnumerator parent;
58
59   GFileAttributeMatcher *matcher;
60   GFileAttributeMatcher *reduced_matcher;
61   char *filename;
62   char *attributes;
63   GFileQueryInfoFlags flags;
64
65   gboolean got_parent_info;
66   GLocalParentFileInfo parent_info;
67   
68 #ifdef USE_GDIR
69   GDir *dir;
70 #else
71   DIR *dir;
72   DirEntry *entries;
73   int entries_pos;
74   gboolean at_end;
75 #endif
76   
77   gboolean follow_symlinks;
78 };
79
80 #define g_local_file_enumerator_get_type _g_local_file_enumerator_get_type
81 G_DEFINE_TYPE (GLocalFileEnumerator, g_local_file_enumerator, G_TYPE_FILE_ENUMERATOR);
82
83 static GFileInfo *g_local_file_enumerator_next_file (GFileEnumerator  *enumerator,
84                                                      GCancellable     *cancellable,
85                                                      GError          **error);
86 static gboolean   g_local_file_enumerator_close     (GFileEnumerator  *enumerator,
87                                                      GCancellable     *cancellable,
88                                                      GError          **error);
89
90
91 static void
92 free_entries (GLocalFileEnumerator *local)
93 {
94 #ifndef USE_GDIR
95   int i;
96
97   if (local->entries != NULL)
98     {
99       for (i = 0; local->entries[i].name != NULL; i++)
100         g_free (local->entries[i].name);
101       
102       g_free (local->entries);
103     }
104 #endif
105 }
106
107 static void
108 g_local_file_enumerator_finalize (GObject *object)
109 {
110   GLocalFileEnumerator *local;
111
112   local = G_LOCAL_FILE_ENUMERATOR (object);
113
114   if (local->got_parent_info)
115     _g_local_file_info_free_parent_info (&local->parent_info);
116   g_free (local->filename);
117   g_file_attribute_matcher_unref (local->matcher);
118   g_file_attribute_matcher_unref (local->reduced_matcher);
119   if (local->dir)
120     {
121 #ifdef USE_GDIR
122       g_dir_close (local->dir);
123 #else
124       closedir (local->dir);
125 #endif      
126       local->dir = NULL;
127     }
128
129   free_entries (local);
130
131   G_OBJECT_CLASS (g_local_file_enumerator_parent_class)->finalize (object);
132 }
133
134
135 static void
136 g_local_file_enumerator_class_init (GLocalFileEnumeratorClass *klass)
137 {
138   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
139   GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
140   
141   gobject_class->finalize = g_local_file_enumerator_finalize;
142
143   enumerator_class->next_file = g_local_file_enumerator_next_file;
144   enumerator_class->close_fn = g_local_file_enumerator_close;
145 }
146
147 static void
148 g_local_file_enumerator_init (GLocalFileEnumerator *local)
149 {
150 }
151
152 #ifdef USE_GDIR
153 static void
154 convert_file_to_io_error (GError **error,
155                           GError  *file_error)
156 {
157   int new_code;
158
159   if (file_error == NULL)
160     return;
161   
162   new_code = G_IO_ERROR_FAILED;
163   
164   if (file_error->domain == G_FILE_ERROR) 
165     {
166       switch (file_error->code) 
167         {
168         case G_FILE_ERROR_NOENT:
169           new_code = G_IO_ERROR_NOT_FOUND;
170           break;
171         case G_FILE_ERROR_ACCES:
172           new_code = G_IO_ERROR_PERMISSION_DENIED;
173           break;
174         case G_FILE_ERROR_NOTDIR:
175           new_code = G_IO_ERROR_NOT_DIRECTORY;
176           break;
177         case G_FILE_ERROR_MFILE:
178           new_code = G_IO_ERROR_TOO_MANY_OPEN_FILES;
179           break;
180         default:
181           break;
182         }
183     }
184   
185   g_set_error_literal (error, G_IO_ERROR,
186                        new_code,
187                        file_error->message);
188 }
189 #else
190 static GFileAttributeMatcher *
191 g_file_attribute_matcher_subtract_attributes (GFileAttributeMatcher *matcher,
192                                               const char *           attributes)
193 {
194   GFileAttributeMatcher *result, *tmp;
195
196   tmp = g_file_attribute_matcher_new (attributes);
197   result = g_file_attribute_matcher_subtract (matcher, tmp);
198   g_file_attribute_matcher_unref (tmp);
199
200   return result;
201 }
202 #endif
203
204 GFileEnumerator *
205 _g_local_file_enumerator_new (GLocalFile *file,
206                               const char           *attributes,
207                               GFileQueryInfoFlags   flags,
208                               GCancellable         *cancellable,
209                               GError              **error)
210 {
211   GLocalFileEnumerator *local;
212   char *filename = g_file_get_path (G_FILE (file));
213
214 #ifdef USE_GDIR
215   GError *dir_error;
216   GDir *dir;
217   
218   dir_error = NULL;
219   dir = g_dir_open (filename, 0, error != NULL ? &dir_error : NULL);
220   if (dir == NULL) 
221     {
222       if (error != NULL)
223         {
224           convert_file_to_io_error (error, dir_error);
225           g_error_free (dir_error);
226         }
227       g_free (filename);
228       return NULL;
229     }
230 #else
231   DIR *dir;
232   int errsv;
233
234   dir = opendir (filename);
235   if (dir == NULL)
236     {
237       errsv = errno;
238
239       g_set_error_literal (error, G_IO_ERROR,
240                            g_io_error_from_errno (errsv),
241                            g_strerror (errsv));
242       g_free (filename);
243       return NULL;
244     }
245
246 #endif
247   
248   local = g_object_new (G_TYPE_LOCAL_FILE_ENUMERATOR,
249                         "container", file,
250                         NULL);
251
252   local->dir = dir;
253   local->filename = filename;
254   local->matcher = g_file_attribute_matcher_new (attributes);
255 #ifndef USE_GDIR
256   local->reduced_matcher = g_file_attribute_matcher_subtract_attributes (local->matcher,
257                                                                          "standard::type,standard::name");
258 #endif
259   local->flags = flags;
260   
261   return G_FILE_ENUMERATOR (local);
262 }
263
264 #ifndef USE_GDIR
265 static int
266 sort_by_inode (const void *_a, const void *_b)
267 {
268   const DirEntry *a, *b;
269
270   a = _a;
271   b = _b;
272   return a->inode - b->inode;
273 }
274
275 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
276 static GFileType
277 file_type_from_dirent (char d_type)
278 {
279   switch (d_type)
280     {
281     case DT_BLK:
282     case DT_CHR:
283     case DT_FIFO:
284     case DT_SOCK:
285       return G_FILE_TYPE_SPECIAL;
286     case DT_DIR:
287       return G_FILE_TYPE_DIRECTORY;
288     case DT_LNK:
289       return G_FILE_TYPE_SYMBOLIC_LINK;
290     case DT_REG:
291       return G_FILE_TYPE_REGULAR;
292     case DT_UNKNOWN:
293     default:
294       return G_FILE_TYPE_UNKNOWN;
295     }
296 }
297 #endif
298
299 static const char *
300 next_file_helper (GLocalFileEnumerator *local, GFileType *file_type)
301 {
302   struct dirent *entry;
303   const char *filename;
304   int i;
305
306   if (local->at_end)
307     return NULL;
308   
309   if (local->entries == NULL ||
310       (local->entries[local->entries_pos].name == NULL))
311     {
312       if (local->entries == NULL)
313         local->entries = g_new (DirEntry, CHUNK_SIZE + 1);
314       else
315         {
316           /* Restart by clearing old names */
317           for (i = 0; local->entries[i].name != NULL; i++)
318             g_free (local->entries[i].name);
319         }
320       
321       for (i = 0; i < CHUNK_SIZE; i++)
322         {
323           entry = readdir (local->dir);
324           while (entry 
325                  && (0 == strcmp (entry->d_name, ".") ||
326                      0 == strcmp (entry->d_name, "..")))
327             entry = readdir (local->dir);
328
329           if (entry)
330             {
331               local->entries[i].name = g_strdup (entry->d_name);
332               local->entries[i].inode = entry->d_ino;
333 #if HAVE_STRUCT_DIRENT_D_TYPE
334               local->entries[i].type = file_type_from_dirent (entry->d_type);
335 #else
336               local->entries[i].type = G_FILE_TYPE_UNKNOWN;
337 #endif
338             }
339           else
340             break;
341         }
342       local->entries[i].name = NULL;
343       local->entries_pos = 0;
344       
345       qsort (local->entries, i, sizeof (DirEntry), sort_by_inode);
346     }
347
348   filename = local->entries[local->entries_pos].name;
349   if (filename == NULL)
350     local->at_end = TRUE;
351     
352   *file_type = local->entries[local->entries_pos].type;
353
354   local->entries_pos++;
355
356   return filename;
357 }
358
359 #endif
360
361 static GFileInfo *
362 g_local_file_enumerator_next_file (GFileEnumerator  *enumerator,
363                                    GCancellable     *cancellable,
364                                    GError          **error)
365 {
366   GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);
367   const char *filename;
368   char *path;
369   GFileInfo *info;
370   GError *my_error;
371   GFileType file_type;
372
373   if (!local->got_parent_info)
374     {
375       _g_local_file_info_get_parent_info (local->filename, local->matcher, &local->parent_info);
376       local->got_parent_info = TRUE;
377     }
378
379  next_file:
380
381 #ifdef USE_GDIR
382   filename = g_dir_read_name (local->dir);
383   file_type = G_FILE_TYPE_UNKNOWN;
384 #else
385   filename = next_file_helper (local, &file_type);
386 #endif
387
388   if (filename == NULL)
389     return NULL;
390
391   my_error = NULL;
392   path = g_build_filename (local->filename, filename, NULL);
393   if (file_type == G_FILE_TYPE_UNKNOWN ||
394       (file_type == G_FILE_TYPE_SYMBOLIC_LINK && !(local->flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
395     {
396       info = _g_local_file_info_get (filename, path,
397                                      local->matcher,
398                                      local->flags,
399                                      &local->parent_info,
400                                      &my_error); 
401     }
402   else
403     {
404       info = _g_local_file_info_get (filename, path,
405                                      local->reduced_matcher,
406                                      local->flags,
407                                      &local->parent_info,
408                                      &my_error); 
409       if (info)
410         {
411           g_file_info_set_name (info, filename);
412           g_file_info_set_file_type (info, file_type);
413           if (file_type == G_FILE_TYPE_SYMBOLIC_LINK)
414             g_file_info_set_is_symlink (info, TRUE);
415         }
416     }
417   g_free (path);
418
419   if (info == NULL)
420     {
421       /* Failed to get info */
422       /* If the file does not exist there might have been a race where
423        * the file was removed between the readdir and the stat, so we
424        * ignore the file. */
425       if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
426         {
427           g_error_free (my_error);
428           goto next_file;
429         }
430       else
431         g_propagate_error (error, my_error);
432     }
433
434   return info;
435 }
436
437 static gboolean
438 g_local_file_enumerator_close (GFileEnumerator  *enumerator,
439                                GCancellable     *cancellable,
440                                GError          **error)
441 {
442   GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);
443
444   if (local->dir)
445     {
446 #ifdef USE_GDIR
447       g_dir_close (local->dir);
448 #else
449       closedir (local->dir);
450 #endif
451       local->dir = NULL;
452     }
453
454   return TRUE;
455 }