1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
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.1 of the License, or (at your option) any later version.
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.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 * Author: Alexander Larsson <alexl@redhat.com>
24 #include <glocalfileenumerator.h>
25 #include <glocalfileinfo.h>
26 #include <glocalfile.h>
33 #define CHUNK_SIZE 1000
41 #include <sys/types.h>
53 struct _GLocalFileEnumerator
55 GFileEnumerator parent
;
57 GFileAttributeMatcher
*matcher
;
58 GFileAttributeMatcher
*reduced_matcher
;
61 GFileQueryInfoFlags flags
;
63 gboolean got_parent_info
;
64 GLocalParentFileInfo parent_info
;
75 gboolean follow_symlinks
;
78 #define g_local_file_enumerator_get_type _g_local_file_enumerator_get_type
79 G_DEFINE_TYPE (GLocalFileEnumerator
, g_local_file_enumerator
, G_TYPE_FILE_ENUMERATOR
)
81 static GFileInfo
*g_local_file_enumerator_next_file (GFileEnumerator
*enumerator
,
82 GCancellable
*cancellable
,
84 static gboolean
g_local_file_enumerator_close (GFileEnumerator
*enumerator
,
85 GCancellable
*cancellable
,
90 free_entries (GLocalFileEnumerator
*local
)
95 if (local
->entries
!= NULL
)
97 for (i
= 0; local
->entries
[i
].name
!= NULL
; i
++)
98 g_free (local
->entries
[i
].name
);
100 g_free (local
->entries
);
106 g_local_file_enumerator_finalize (GObject
*object
)
108 GLocalFileEnumerator
*local
;
110 local
= G_LOCAL_FILE_ENUMERATOR (object
);
112 if (local
->got_parent_info
)
113 _g_local_file_info_free_parent_info (&local
->parent_info
);
114 g_free (local
->filename
);
115 g_file_attribute_matcher_unref (local
->matcher
);
116 g_file_attribute_matcher_unref (local
->reduced_matcher
);
120 g_dir_close (local
->dir
);
122 closedir (local
->dir
);
127 free_entries (local
);
129 G_OBJECT_CLASS (g_local_file_enumerator_parent_class
)->finalize (object
);
134 g_local_file_enumerator_class_init (GLocalFileEnumeratorClass
*klass
)
136 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
137 GFileEnumeratorClass
*enumerator_class
= G_FILE_ENUMERATOR_CLASS (klass
);
139 gobject_class
->finalize
= g_local_file_enumerator_finalize
;
141 enumerator_class
->next_file
= g_local_file_enumerator_next_file
;
142 enumerator_class
->close_fn
= g_local_file_enumerator_close
;
146 g_local_file_enumerator_init (GLocalFileEnumerator
*local
)
152 convert_file_to_io_error (GError
**error
,
157 if (file_error
== NULL
)
160 new_code
= G_IO_ERROR_FAILED
;
162 if (file_error
->domain
== G_FILE_ERROR
)
164 switch (file_error
->code
)
166 case G_FILE_ERROR_NOENT
:
167 new_code
= G_IO_ERROR_NOT_FOUND
;
169 case G_FILE_ERROR_ACCES
:
170 new_code
= G_IO_ERROR_PERMISSION_DENIED
;
172 case G_FILE_ERROR_NOTDIR
:
173 new_code
= G_IO_ERROR_NOT_DIRECTORY
;
175 case G_FILE_ERROR_MFILE
:
176 new_code
= G_IO_ERROR_TOO_MANY_OPEN_FILES
;
183 g_set_error_literal (error
, G_IO_ERROR
,
185 file_error
->message
);
188 static GFileAttributeMatcher
*
189 g_file_attribute_matcher_subtract_attributes (GFileAttributeMatcher
*matcher
,
190 const char * attributes
)
192 GFileAttributeMatcher
*result
, *tmp
;
194 tmp
= g_file_attribute_matcher_new (attributes
);
195 result
= g_file_attribute_matcher_subtract (matcher
, tmp
);
196 g_file_attribute_matcher_unref (tmp
);
203 _g_local_file_enumerator_new (GLocalFile
*file
,
204 const char *attributes
,
205 GFileQueryInfoFlags flags
,
206 GCancellable
*cancellable
,
209 GLocalFileEnumerator
*local
;
210 char *filename
= g_file_get_path (G_FILE (file
));
217 dir
= g_dir_open (filename
, 0, error
!= NULL
? &dir_error
: NULL
);
222 convert_file_to_io_error (error
, dir_error
);
223 g_error_free (dir_error
);
232 dir
= opendir (filename
);
235 gchar
*utf8_filename
;
238 utf8_filename
= g_filename_to_utf8 (filename
, -1, NULL
, NULL
, NULL
);
239 g_set_error (error
, G_IO_ERROR
,
240 g_io_error_from_errno (errsv
),
241 "Error opening directory '%s': %s",
242 utf8_filename
, g_strerror (errsv
));
243 g_free (utf8_filename
);
250 local
= g_object_new (G_TYPE_LOCAL_FILE_ENUMERATOR
,
255 local
->filename
= filename
;
256 local
->matcher
= g_file_attribute_matcher_new (attributes
);
258 local
->reduced_matcher
= g_file_attribute_matcher_subtract_attributes (local
->matcher
,
259 G_LOCAL_FILE_INFO_NOSTAT_ATTRIBUTES
","
262 local
->flags
= flags
;
264 return G_FILE_ENUMERATOR (local
);
269 sort_by_inode (const void *_a
, const void *_b
)
271 const DirEntry
*a
, *b
;
275 return a
->inode
- b
->inode
;
278 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
280 file_type_from_dirent (char d_type
)
288 return G_FILE_TYPE_SPECIAL
;
290 return G_FILE_TYPE_DIRECTORY
;
292 return G_FILE_TYPE_SYMBOLIC_LINK
;
294 return G_FILE_TYPE_REGULAR
;
297 return G_FILE_TYPE_UNKNOWN
;
303 next_file_helper (GLocalFileEnumerator
*local
, GFileType
*file_type
)
305 struct dirent
*entry
;
306 const char *filename
;
312 if (local
->entries
== NULL
||
313 (local
->entries
[local
->entries_pos
].name
== NULL
))
315 if (local
->entries
== NULL
)
316 local
->entries
= g_new (DirEntry
, CHUNK_SIZE
+ 1);
319 /* Restart by clearing old names */
320 for (i
= 0; local
->entries
[i
].name
!= NULL
; i
++)
321 g_free (local
->entries
[i
].name
);
324 for (i
= 0; i
< CHUNK_SIZE
; i
++)
326 entry
= readdir (local
->dir
);
328 && (0 == strcmp (entry
->d_name
, ".") ||
329 0 == strcmp (entry
->d_name
, "..")))
330 entry
= readdir (local
->dir
);
334 local
->entries
[i
].name
= g_strdup (entry
->d_name
);
335 local
->entries
[i
].inode
= entry
->d_ino
;
336 #if HAVE_STRUCT_DIRENT_D_TYPE
337 local
->entries
[i
].type
= file_type_from_dirent (entry
->d_type
);
339 local
->entries
[i
].type
= G_FILE_TYPE_UNKNOWN
;
345 local
->entries
[i
].name
= NULL
;
346 local
->entries_pos
= 0;
348 qsort (local
->entries
, i
, sizeof (DirEntry
), sort_by_inode
);
351 filename
= local
->entries
[local
->entries_pos
].name
;
352 if (filename
== NULL
)
353 local
->at_end
= TRUE
;
355 *file_type
= local
->entries
[local
->entries_pos
].type
;
357 local
->entries_pos
++;
365 g_local_file_enumerator_next_file (GFileEnumerator
*enumerator
,
366 GCancellable
*cancellable
,
369 GLocalFileEnumerator
*local
= G_LOCAL_FILE_ENUMERATOR (enumerator
);
370 const char *filename
;
376 if (!local
->got_parent_info
)
378 _g_local_file_info_get_parent_info (local
->filename
, local
->matcher
, &local
->parent_info
);
379 local
->got_parent_info
= TRUE
;
385 filename
= g_dir_read_name (local
->dir
);
386 file_type
= G_FILE_TYPE_UNKNOWN
;
388 filename
= next_file_helper (local
, &file_type
);
391 if (filename
== NULL
)
395 path
= g_build_filename (local
->filename
, filename
, NULL
);
396 if (file_type
== G_FILE_TYPE_UNKNOWN
||
397 (file_type
== G_FILE_TYPE_SYMBOLIC_LINK
&& !(local
->flags
& G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
)))
399 info
= _g_local_file_info_get (filename
, path
,
407 info
= _g_local_file_info_get (filename
, path
,
408 local
->reduced_matcher
,
414 _g_local_file_info_get_nostat (info
, filename
, path
, local
->matcher
);
415 g_file_info_set_file_type (info
, file_type
);
416 if (file_type
== G_FILE_TYPE_SYMBOLIC_LINK
)
417 g_file_info_set_is_symlink (info
, TRUE
);
424 /* Failed to get info */
425 /* If the file does not exist there might have been a race where
426 * the file was removed between the readdir and the stat, so we
427 * ignore the file. */
428 if (g_error_matches (my_error
, G_IO_ERROR
, G_IO_ERROR_NOT_FOUND
))
430 g_error_free (my_error
);
434 g_propagate_error (error
, my_error
);
441 g_local_file_enumerator_close (GFileEnumerator
*enumerator
,
442 GCancellable
*cancellable
,
445 GLocalFileEnumerator
*local
= G_LOCAL_FILE_ENUMERATOR (enumerator
);
450 g_dir_close (local
->dir
);
452 closedir (local
->dir
);