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 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
);
237 g_set_error_literal (error
, G_IO_ERROR
,
238 g_io_error_from_errno (errsv
),
246 local
= g_object_new (G_TYPE_LOCAL_FILE_ENUMERATOR
,
251 local
->filename
= filename
;
252 local
->matcher
= g_file_attribute_matcher_new (attributes
);
254 local
->reduced_matcher
= g_file_attribute_matcher_subtract_attributes (local
->matcher
,
255 G_LOCAL_FILE_INFO_NOSTAT_ATTRIBUTES
","
258 local
->flags
= flags
;
260 return G_FILE_ENUMERATOR (local
);
265 sort_by_inode (const void *_a
, const void *_b
)
267 const DirEntry
*a
, *b
;
271 return a
->inode
- b
->inode
;
274 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
276 file_type_from_dirent (char d_type
)
284 return G_FILE_TYPE_SPECIAL
;
286 return G_FILE_TYPE_DIRECTORY
;
288 return G_FILE_TYPE_SYMBOLIC_LINK
;
290 return G_FILE_TYPE_REGULAR
;
293 return G_FILE_TYPE_UNKNOWN
;
299 next_file_helper (GLocalFileEnumerator
*local
, GFileType
*file_type
)
301 struct dirent
*entry
;
302 const char *filename
;
308 if (local
->entries
== NULL
||
309 (local
->entries
[local
->entries_pos
].name
== NULL
))
311 if (local
->entries
== NULL
)
312 local
->entries
= g_new (DirEntry
, CHUNK_SIZE
+ 1);
315 /* Restart by clearing old names */
316 for (i
= 0; local
->entries
[i
].name
!= NULL
; i
++)
317 g_free (local
->entries
[i
].name
);
320 for (i
= 0; i
< CHUNK_SIZE
; i
++)
322 entry
= readdir (local
->dir
);
324 && (0 == strcmp (entry
->d_name
, ".") ||
325 0 == strcmp (entry
->d_name
, "..")))
326 entry
= readdir (local
->dir
);
330 local
->entries
[i
].name
= g_strdup (entry
->d_name
);
331 local
->entries
[i
].inode
= entry
->d_ino
;
332 #if HAVE_STRUCT_DIRENT_D_TYPE
333 local
->entries
[i
].type
= file_type_from_dirent (entry
->d_type
);
335 local
->entries
[i
].type
= G_FILE_TYPE_UNKNOWN
;
341 local
->entries
[i
].name
= NULL
;
342 local
->entries_pos
= 0;
344 qsort (local
->entries
, i
, sizeof (DirEntry
), sort_by_inode
);
347 filename
= local
->entries
[local
->entries_pos
].name
;
348 if (filename
== NULL
)
349 local
->at_end
= TRUE
;
351 *file_type
= local
->entries
[local
->entries_pos
].type
;
353 local
->entries_pos
++;
361 g_local_file_enumerator_next_file (GFileEnumerator
*enumerator
,
362 GCancellable
*cancellable
,
365 GLocalFileEnumerator
*local
= G_LOCAL_FILE_ENUMERATOR (enumerator
);
366 const char *filename
;
372 if (!local
->got_parent_info
)
374 _g_local_file_info_get_parent_info (local
->filename
, local
->matcher
, &local
->parent_info
);
375 local
->got_parent_info
= TRUE
;
381 filename
= g_dir_read_name (local
->dir
);
382 file_type
= G_FILE_TYPE_UNKNOWN
;
384 filename
= next_file_helper (local
, &file_type
);
387 if (filename
== NULL
)
391 path
= g_build_filename (local
->filename
, filename
, NULL
);
392 if (file_type
== G_FILE_TYPE_UNKNOWN
||
393 (file_type
== G_FILE_TYPE_SYMBOLIC_LINK
&& !(local
->flags
& G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
)))
395 info
= _g_local_file_info_get (filename
, path
,
403 info
= _g_local_file_info_get (filename
, path
,
404 local
->reduced_matcher
,
410 _g_local_file_info_get_nostat (info
, filename
, path
, local
->matcher
);
411 g_file_info_set_file_type (info
, file_type
);
412 if (file_type
== G_FILE_TYPE_SYMBOLIC_LINK
)
413 g_file_info_set_is_symlink (info
, TRUE
);
420 /* Failed to get info */
421 /* If the file does not exist there might have been a race where
422 * the file was removed between the readdir and the stat, so we
423 * ignore the file. */
424 if (g_error_matches (my_error
, G_IO_ERROR
, G_IO_ERROR_NOT_FOUND
))
426 g_error_free (my_error
);
430 g_propagate_error (error
, my_error
);
437 g_local_file_enumerator_close (GFileEnumerator
*enumerator
,
438 GCancellable
*cancellable
,
441 GLocalFileEnumerator
*local
= G_LOCAL_FILE_ENUMERATOR (enumerator
);
446 g_dir_close (local
->dir
);
448 closedir (local
->dir
);