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.1 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, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | * Author: Alexander Larsson <alexl@redhat.com> |
19 | */ |
20 | |
21 | #include "config.h" |
22 | #include "gfileenumerator.h" |
23 | #include "gfile.h" |
24 | #include "gioscheduler.h" |
25 | #include "gasyncresult.h" |
26 | #include "gasynchelper.h" |
27 | #include "gioerror.h" |
28 | #include "glibintl.h" |
29 | |
30 | struct _GFileEnumeratorPrivate { |
31 | /* TODO: Should be public for subclasses? */ |
32 | GFile *container; |
33 | guint closed : 1; |
34 | guint pending : 1; |
35 | GAsyncReadyCallback outstanding_callback; |
36 | GError *outstanding_error; |
37 | }; |
38 | |
39 | /** |
40 | * SECTION:gfileenumerator |
41 | * @short_description: Enumerated Files Routines |
42 | * @include: gio/gio.h |
43 | * |
44 | * #GFileEnumerator allows you to operate on a set of #GFiles, |
45 | * returning a #GFileInfo structure for each file enumerated (e.g. |
46 | * g_file_enumerate_children() will return a #GFileEnumerator for each |
47 | * of the children within a directory). |
48 | * |
49 | * To get the next file's information from a #GFileEnumerator, use |
50 | * g_file_enumerator_next_file() or its asynchronous version, |
51 | * g_file_enumerator_next_files_async(). Note that the asynchronous |
52 | * version will return a list of #GFileInfos, whereas the |
53 | * synchronous will only return the next file in the enumerator. |
54 | * |
55 | * The ordering of returned files is unspecified for non-Unix |
56 | * platforms; for more information, see g_dir_read_name(). On Unix, |
57 | * when operating on local files, returned files will be sorted by |
58 | * inode number. Effectively you can assume that the ordering of |
59 | * returned files will be stable between successive calls (and |
60 | * applications) assuming the directory is unchanged. |
61 | * |
62 | * If your application needs a specific ordering, such as by name or |
63 | * modification time, you will have to implement that in your |
64 | * application code. |
65 | * |
66 | * To close a #GFileEnumerator, use g_file_enumerator_close(), or |
67 | * its asynchronous version, g_file_enumerator_close_async(). Once |
68 | * a #GFileEnumerator is closed, no further actions may be performed |
69 | * on it, and it should be freed with g_object_unref(). |
70 | * |
71 | **/ |
72 | |
73 | G_DEFINE_TYPE_WITH_PRIVATE (GFileEnumerator, g_file_enumerator, G_TYPE_OBJECT) |
74 | |
75 | enum { |
76 | PROP_0, |
77 | PROP_CONTAINER |
78 | }; |
79 | |
80 | static void g_file_enumerator_real_next_files_async (GFileEnumerator *enumerator, |
81 | int num_files, |
82 | int io_priority, |
83 | GCancellable *cancellable, |
84 | GAsyncReadyCallback callback, |
85 | gpointer user_data); |
86 | static GList * g_file_enumerator_real_next_files_finish (GFileEnumerator *enumerator, |
87 | GAsyncResult *res, |
88 | GError **error); |
89 | static void g_file_enumerator_real_close_async (GFileEnumerator *enumerator, |
90 | int io_priority, |
91 | GCancellable *cancellable, |
92 | GAsyncReadyCallback callback, |
93 | gpointer user_data); |
94 | static gboolean g_file_enumerator_real_close_finish (GFileEnumerator *enumerator, |
95 | GAsyncResult *res, |
96 | GError **error); |
97 | |
98 | static void |
99 | g_file_enumerator_set_property (GObject *object, |
100 | guint property_id, |
101 | const GValue *value, |
102 | GParamSpec *pspec) |
103 | { |
104 | GFileEnumerator *enumerator; |
105 | |
106 | enumerator = G_FILE_ENUMERATOR (object); |
107 | |
108 | switch (property_id) { |
109 | case PROP_CONTAINER: |
110 | enumerator->priv->container = g_value_dup_object (value); |
111 | break; |
112 | default: |
113 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
114 | break; |
115 | } |
116 | } |
117 | |
118 | static void |
119 | g_file_enumerator_dispose (GObject *object) |
120 | { |
121 | GFileEnumerator *enumerator; |
122 | |
123 | enumerator = G_FILE_ENUMERATOR (object); |
124 | |
125 | if (enumerator->priv->container) { |
126 | g_object_unref (object: enumerator->priv->container); |
127 | enumerator->priv->container = NULL; |
128 | } |
129 | |
130 | G_OBJECT_CLASS (g_file_enumerator_parent_class)->dispose (object); |
131 | } |
132 | |
133 | static void |
134 | g_file_enumerator_finalize (GObject *object) |
135 | { |
136 | GFileEnumerator *enumerator; |
137 | |
138 | enumerator = G_FILE_ENUMERATOR (object); |
139 | |
140 | if (!enumerator->priv->closed) |
141 | g_file_enumerator_close (enumerator, NULL, NULL); |
142 | |
143 | G_OBJECT_CLASS (g_file_enumerator_parent_class)->finalize (object); |
144 | } |
145 | |
146 | static void |
147 | g_file_enumerator_class_init (GFileEnumeratorClass *klass) |
148 | { |
149 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
150 | |
151 | gobject_class->set_property = g_file_enumerator_set_property; |
152 | gobject_class->dispose = g_file_enumerator_dispose; |
153 | gobject_class->finalize = g_file_enumerator_finalize; |
154 | |
155 | klass->next_files_async = g_file_enumerator_real_next_files_async; |
156 | klass->next_files_finish = g_file_enumerator_real_next_files_finish; |
157 | klass->close_async = g_file_enumerator_real_close_async; |
158 | klass->close_finish = g_file_enumerator_real_close_finish; |
159 | |
160 | g_object_class_install_property |
161 | (oclass: gobject_class, property_id: PROP_CONTAINER, |
162 | pspec: g_param_spec_object (name: "container" , P_("Container" ), |
163 | P_("The container that is being enumerated" ), |
164 | G_TYPE_FILE, |
165 | flags: G_PARAM_WRITABLE | |
166 | G_PARAM_CONSTRUCT_ONLY | |
167 | G_PARAM_STATIC_STRINGS)); |
168 | } |
169 | |
170 | static void |
171 | g_file_enumerator_init (GFileEnumerator *enumerator) |
172 | { |
173 | enumerator->priv = g_file_enumerator_get_instance_private (self: enumerator); |
174 | } |
175 | |
176 | /** |
177 | * g_file_enumerator_next_file: |
178 | * @enumerator: a #GFileEnumerator. |
179 | * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. |
180 | * @error: location to store the error occurring, or %NULL to ignore |
181 | * |
182 | * Returns information for the next file in the enumerated object. |
183 | * Will block until the information is available. The #GFileInfo |
184 | * returned from this function will contain attributes that match the |
185 | * attribute string that was passed when the #GFileEnumerator was created. |
186 | * |
187 | * See the documentation of #GFileEnumerator for information about the |
188 | * order of returned files. |
189 | * |
190 | * On error, returns %NULL and sets @error to the error. If the |
191 | * enumerator is at the end, %NULL will be returned and @error will |
192 | * be unset. |
193 | * |
194 | * Returns: (nullable) (transfer full): A #GFileInfo or %NULL on error |
195 | * or end of enumerator. Free the returned object with |
196 | * g_object_unref() when no longer needed. |
197 | **/ |
198 | GFileInfo * |
199 | g_file_enumerator_next_file (GFileEnumerator *enumerator, |
200 | GCancellable *cancellable, |
201 | GError **error) |
202 | { |
203 | GFileEnumeratorClass *class; |
204 | GFileInfo *info; |
205 | |
206 | g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL); |
207 | g_return_val_if_fail (enumerator != NULL, NULL); |
208 | |
209 | if (enumerator->priv->closed) |
210 | { |
211 | g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_CLOSED, |
212 | _("Enumerator is closed" )); |
213 | return NULL; |
214 | } |
215 | |
216 | if (enumerator->priv->pending) |
217 | { |
218 | g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_PENDING, |
219 | _("File enumerator has outstanding operation" )); |
220 | return NULL; |
221 | } |
222 | |
223 | if (enumerator->priv->outstanding_error) |
224 | { |
225 | g_propagate_error (dest: error, src: enumerator->priv->outstanding_error); |
226 | enumerator->priv->outstanding_error = NULL; |
227 | return NULL; |
228 | } |
229 | |
230 | class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
231 | |
232 | if (cancellable) |
233 | g_cancellable_push_current (cancellable); |
234 | |
235 | enumerator->priv->pending = TRUE; |
236 | info = (* class->next_file) (enumerator, cancellable, error); |
237 | enumerator->priv->pending = FALSE; |
238 | |
239 | if (cancellable) |
240 | g_cancellable_pop_current (cancellable); |
241 | |
242 | return info; |
243 | } |
244 | |
245 | /** |
246 | * g_file_enumerator_close: |
247 | * @enumerator: a #GFileEnumerator. |
248 | * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. |
249 | * @error: location to store the error occurring, or %NULL to ignore |
250 | * |
251 | * Releases all resources used by this enumerator, making the |
252 | * enumerator return %G_IO_ERROR_CLOSED on all calls. |
253 | * |
254 | * This will be automatically called when the last reference |
255 | * is dropped, but you might want to call this function to make |
256 | * sure resources are released as early as possible. |
257 | * |
258 | * Returns: #TRUE on success or #FALSE on error. |
259 | **/ |
260 | gboolean |
261 | g_file_enumerator_close (GFileEnumerator *enumerator, |
262 | GCancellable *cancellable, |
263 | GError **error) |
264 | { |
265 | GFileEnumeratorClass *class; |
266 | |
267 | g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), FALSE); |
268 | g_return_val_if_fail (enumerator != NULL, FALSE); |
269 | |
270 | class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
271 | |
272 | if (enumerator->priv->closed) |
273 | return TRUE; |
274 | |
275 | if (enumerator->priv->pending) |
276 | { |
277 | g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_PENDING, |
278 | _("File enumerator has outstanding operation" )); |
279 | return FALSE; |
280 | } |
281 | |
282 | if (cancellable) |
283 | g_cancellable_push_current (cancellable); |
284 | |
285 | enumerator->priv->pending = TRUE; |
286 | (* class->close_fn) (enumerator, cancellable, error); |
287 | enumerator->priv->pending = FALSE; |
288 | enumerator->priv->closed = TRUE; |
289 | |
290 | if (cancellable) |
291 | g_cancellable_pop_current (cancellable); |
292 | |
293 | return TRUE; |
294 | } |
295 | |
296 | static void |
297 | next_async_callback_wrapper (GObject *source_object, |
298 | GAsyncResult *res, |
299 | gpointer user_data) |
300 | { |
301 | GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source_object); |
302 | |
303 | enumerator->priv->pending = FALSE; |
304 | if (enumerator->priv->outstanding_callback) |
305 | (*enumerator->priv->outstanding_callback) (source_object, res, user_data); |
306 | g_object_unref (object: enumerator); |
307 | } |
308 | |
309 | /** |
310 | * g_file_enumerator_next_files_async: |
311 | * @enumerator: a #GFileEnumerator. |
312 | * @num_files: the number of file info objects to request |
313 | * @io_priority: the [I/O priority][io-priority] of the request |
314 | * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. |
315 | * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied |
316 | * @user_data: (closure): the data to pass to callback function |
317 | * |
318 | * Request information for a number of files from the enumerator asynchronously. |
319 | * When all i/o for the operation is finished the @callback will be called with |
320 | * the requested information. |
321 | * |
322 | * See the documentation of #GFileEnumerator for information about the |
323 | * order of returned files. |
324 | * |
325 | * The callback can be called with less than @num_files files in case of error |
326 | * or at the end of the enumerator. In case of a partial error the callback will |
327 | * be called with any succeeding items and no error, and on the next request the |
328 | * error will be reported. If a request is cancelled the callback will be called |
329 | * with %G_IO_ERROR_CANCELLED. |
330 | * |
331 | * During an async request no other sync and async calls are allowed, and will |
332 | * result in %G_IO_ERROR_PENDING errors. |
333 | * |
334 | * Any outstanding i/o request with higher priority (lower numerical value) will |
335 | * be executed before an outstanding request with lower priority. Default |
336 | * priority is %G_PRIORITY_DEFAULT. |
337 | **/ |
338 | void |
339 | g_file_enumerator_next_files_async (GFileEnumerator *enumerator, |
340 | int num_files, |
341 | int io_priority, |
342 | GCancellable *cancellable, |
343 | GAsyncReadyCallback callback, |
344 | gpointer user_data) |
345 | { |
346 | GFileEnumeratorClass *class; |
347 | |
348 | g_return_if_fail (G_IS_FILE_ENUMERATOR (enumerator)); |
349 | g_return_if_fail (enumerator != NULL); |
350 | g_return_if_fail (num_files >= 0); |
351 | |
352 | if (num_files == 0) |
353 | { |
354 | GTask *task; |
355 | |
356 | task = g_task_new (source_object: enumerator, cancellable, callback, callback_data: user_data); |
357 | g_task_set_source_tag (task, g_file_enumerator_next_files_async); |
358 | g_task_return_pointer (task, NULL, NULL); |
359 | g_object_unref (object: task); |
360 | return; |
361 | } |
362 | |
363 | if (enumerator->priv->closed) |
364 | { |
365 | g_task_report_new_error (source_object: enumerator, callback, callback_data: user_data, |
366 | source_tag: g_file_enumerator_next_files_async, |
367 | G_IO_ERROR, code: G_IO_ERROR_CLOSED, |
368 | _("File enumerator is already closed" )); |
369 | return; |
370 | } |
371 | |
372 | if (enumerator->priv->pending) |
373 | { |
374 | g_task_report_new_error (source_object: enumerator, callback, callback_data: user_data, |
375 | source_tag: g_file_enumerator_next_files_async, |
376 | G_IO_ERROR, code: G_IO_ERROR_PENDING, |
377 | _("File enumerator has outstanding operation" )); |
378 | return; |
379 | } |
380 | |
381 | class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
382 | |
383 | enumerator->priv->pending = TRUE; |
384 | enumerator->priv->outstanding_callback = callback; |
385 | g_object_ref (enumerator); |
386 | (* class->next_files_async) (enumerator, num_files, io_priority, cancellable, |
387 | next_async_callback_wrapper, user_data); |
388 | } |
389 | |
390 | /** |
391 | * g_file_enumerator_next_files_finish: |
392 | * @enumerator: a #GFileEnumerator. |
393 | * @result: a #GAsyncResult. |
394 | * @error: a #GError location to store the error occurring, or %NULL to |
395 | * ignore. |
396 | * |
397 | * Finishes the asynchronous operation started with g_file_enumerator_next_files_async(). |
398 | * |
399 | * Returns: (transfer full) (element-type Gio.FileInfo): a #GList of #GFileInfos. You must free the list with |
400 | * g_list_free() and unref the infos with g_object_unref() when you're |
401 | * done with them. |
402 | **/ |
403 | GList * |
404 | g_file_enumerator_next_files_finish (GFileEnumerator *enumerator, |
405 | GAsyncResult *result, |
406 | GError **error) |
407 | { |
408 | GFileEnumeratorClass *class; |
409 | |
410 | g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL); |
411 | g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL); |
412 | |
413 | if (g_async_result_legacy_propagate_error (res: result, error)) |
414 | return NULL; |
415 | else if (g_async_result_is_tagged (res: result, source_tag: g_file_enumerator_next_files_async)) |
416 | return g_task_propagate_pointer (G_TASK (result), error); |
417 | |
418 | class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
419 | return class->next_files_finish (enumerator, result, error); |
420 | } |
421 | |
422 | static void |
423 | close_async_callback_wrapper (GObject *source_object, |
424 | GAsyncResult *res, |
425 | gpointer user_data) |
426 | { |
427 | GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source_object); |
428 | |
429 | enumerator->priv->pending = FALSE; |
430 | enumerator->priv->closed = TRUE; |
431 | if (enumerator->priv->outstanding_callback) |
432 | (*enumerator->priv->outstanding_callback) (source_object, res, user_data); |
433 | g_object_unref (object: enumerator); |
434 | } |
435 | |
436 | /** |
437 | * g_file_enumerator_close_async: |
438 | * @enumerator: a #GFileEnumerator. |
439 | * @io_priority: the [I/O priority][io-priority] of the request |
440 | * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. |
441 | * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied |
442 | * @user_data: (closure): the data to pass to callback function |
443 | * |
444 | * Asynchronously closes the file enumerator. |
445 | * |
446 | * If @cancellable is not %NULL, then the operation can be cancelled by |
447 | * triggering the cancellable object from another thread. If the operation |
448 | * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned in |
449 | * g_file_enumerator_close_finish(). |
450 | **/ |
451 | void |
452 | g_file_enumerator_close_async (GFileEnumerator *enumerator, |
453 | int io_priority, |
454 | GCancellable *cancellable, |
455 | GAsyncReadyCallback callback, |
456 | gpointer user_data) |
457 | { |
458 | GFileEnumeratorClass *class; |
459 | |
460 | g_return_if_fail (G_IS_FILE_ENUMERATOR (enumerator)); |
461 | |
462 | if (enumerator->priv->closed) |
463 | { |
464 | g_task_report_new_error (source_object: enumerator, callback, callback_data: user_data, |
465 | source_tag: g_file_enumerator_close_async, |
466 | G_IO_ERROR, code: G_IO_ERROR_CLOSED, |
467 | _("File enumerator is already closed" )); |
468 | return; |
469 | } |
470 | |
471 | if (enumerator->priv->pending) |
472 | { |
473 | g_task_report_new_error (source_object: enumerator, callback, callback_data: user_data, |
474 | source_tag: g_file_enumerator_close_async, |
475 | G_IO_ERROR, code: G_IO_ERROR_PENDING, |
476 | _("File enumerator has outstanding operation" )); |
477 | return; |
478 | } |
479 | |
480 | class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
481 | |
482 | enumerator->priv->pending = TRUE; |
483 | enumerator->priv->outstanding_callback = callback; |
484 | g_object_ref (enumerator); |
485 | (* class->close_async) (enumerator, io_priority, cancellable, |
486 | close_async_callback_wrapper, user_data); |
487 | } |
488 | |
489 | /** |
490 | * g_file_enumerator_close_finish: |
491 | * @enumerator: a #GFileEnumerator. |
492 | * @result: a #GAsyncResult. |
493 | * @error: a #GError location to store the error occurring, or %NULL to |
494 | * ignore. |
495 | * |
496 | * Finishes closing a file enumerator, started from g_file_enumerator_close_async(). |
497 | * |
498 | * If the file enumerator was already closed when g_file_enumerator_close_async() |
499 | * was called, then this function will report %G_IO_ERROR_CLOSED in @error, and |
500 | * return %FALSE. If the file enumerator had pending operation when the close |
501 | * operation was started, then this function will report %G_IO_ERROR_PENDING, and |
502 | * return %FALSE. If @cancellable was not %NULL, then the operation may have been |
503 | * cancelled by triggering the cancellable object from another thread. If the operation |
504 | * was cancelled, the error %G_IO_ERROR_CANCELLED will be set, and %FALSE will be |
505 | * returned. |
506 | * |
507 | * Returns: %TRUE if the close operation has finished successfully. |
508 | **/ |
509 | gboolean |
510 | g_file_enumerator_close_finish (GFileEnumerator *enumerator, |
511 | GAsyncResult *result, |
512 | GError **error) |
513 | { |
514 | GFileEnumeratorClass *class; |
515 | |
516 | g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), FALSE); |
517 | g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); |
518 | |
519 | if (g_async_result_legacy_propagate_error (res: result, error)) |
520 | return FALSE; |
521 | else if (g_async_result_is_tagged (res: result, source_tag: g_file_enumerator_close_async)) |
522 | return g_task_propagate_boolean (G_TASK (result), error); |
523 | |
524 | class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
525 | return class->close_finish (enumerator, result, error); |
526 | } |
527 | |
528 | /** |
529 | * g_file_enumerator_is_closed: |
530 | * @enumerator: a #GFileEnumerator. |
531 | * |
532 | * Checks if the file enumerator has been closed. |
533 | * |
534 | * Returns: %TRUE if the @enumerator is closed. |
535 | **/ |
536 | gboolean |
537 | g_file_enumerator_is_closed (GFileEnumerator *enumerator) |
538 | { |
539 | g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), TRUE); |
540 | |
541 | return enumerator->priv->closed; |
542 | } |
543 | |
544 | /** |
545 | * g_file_enumerator_has_pending: |
546 | * @enumerator: a #GFileEnumerator. |
547 | * |
548 | * Checks if the file enumerator has pending operations. |
549 | * |
550 | * Returns: %TRUE if the @enumerator has pending operations. |
551 | **/ |
552 | gboolean |
553 | g_file_enumerator_has_pending (GFileEnumerator *enumerator) |
554 | { |
555 | g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), TRUE); |
556 | |
557 | return enumerator->priv->pending; |
558 | } |
559 | |
560 | /** |
561 | * g_file_enumerator_set_pending: |
562 | * @enumerator: a #GFileEnumerator. |
563 | * @pending: a boolean value. |
564 | * |
565 | * Sets the file enumerator as having pending operations. |
566 | **/ |
567 | void |
568 | g_file_enumerator_set_pending (GFileEnumerator *enumerator, |
569 | gboolean pending) |
570 | { |
571 | g_return_if_fail (G_IS_FILE_ENUMERATOR (enumerator)); |
572 | |
573 | enumerator->priv->pending = pending; |
574 | } |
575 | |
576 | /** |
577 | * g_file_enumerator_iterate: |
578 | * @direnum: an open #GFileEnumerator |
579 | * @out_info: (out) (transfer none) (optional): Output location for the next #GFileInfo, or %NULL |
580 | * @out_child: (out) (transfer none) (optional): Output location for the next #GFile, or %NULL |
581 | * @cancellable: a #GCancellable |
582 | * @error: a #GError |
583 | * |
584 | * This is a version of g_file_enumerator_next_file() that's easier to |
585 | * use correctly from C programs. With g_file_enumerator_next_file(), |
586 | * the gboolean return value signifies "end of iteration or error", which |
587 | * requires allocation of a temporary #GError. |
588 | * |
589 | * In contrast, with this function, a %FALSE return from |
590 | * g_file_enumerator_iterate() *always* means |
591 | * "error". End of iteration is signaled by @out_info or @out_child being %NULL. |
592 | * |
593 | * Another crucial difference is that the references for @out_info and |
594 | * @out_child are owned by @direnum (they are cached as hidden |
595 | * properties). You must not unref them in your own code. This makes |
596 | * memory management significantly easier for C code in combination |
597 | * with loops. |
598 | * |
599 | * Finally, this function optionally allows retrieving a #GFile as |
600 | * well. |
601 | * |
602 | * You must specify at least one of @out_info or @out_child. |
603 | * |
604 | * The code pattern for correctly using g_file_enumerator_iterate() from C |
605 | * is: |
606 | * |
607 | * |[ |
608 | * direnum = g_file_enumerate_children (file, ...); |
609 | * while (TRUE) |
610 | * { |
611 | * GFileInfo *info; |
612 | * if (!g_file_enumerator_iterate (direnum, &info, NULL, cancellable, error)) |
613 | * goto out; |
614 | * if (!info) |
615 | * break; |
616 | * ... do stuff with "info"; do not unref it! ... |
617 | * } |
618 | * |
619 | * out: |
620 | * g_object_unref (direnum); // Note: frees the last @info |
621 | * ]| |
622 | * |
623 | * |
624 | * Since: 2.44 |
625 | */ |
626 | gboolean |
627 | g_file_enumerator_iterate (GFileEnumerator *direnum, |
628 | GFileInfo **out_info, |
629 | GFile **out_child, |
630 | GCancellable *cancellable, |
631 | GError **error) |
632 | { |
633 | gboolean ret = FALSE; |
634 | GError *temp_error = NULL; |
635 | GFileInfo *ret_info = NULL; |
636 | |
637 | static GQuark cached_info_quark; |
638 | static GQuark cached_child_quark; |
639 | static gsize quarks_initialized; |
640 | |
641 | g_return_val_if_fail (direnum != NULL, FALSE); |
642 | g_return_val_if_fail (out_info != NULL || out_child != NULL, FALSE); |
643 | |
644 | if (g_once_init_enter (&quarks_initialized)) |
645 | { |
646 | cached_info_quark = g_quark_from_static_string (string: "g-cached-info" ); |
647 | cached_child_quark = g_quark_from_static_string (string: "g-cached-child" ); |
648 | g_once_init_leave (&quarks_initialized, 1); |
649 | } |
650 | |
651 | ret_info = g_file_enumerator_next_file (enumerator: direnum, cancellable, error: &temp_error); |
652 | if (temp_error != NULL) |
653 | { |
654 | g_propagate_error (dest: error, src: temp_error); |
655 | goto out; |
656 | } |
657 | |
658 | if (ret_info) |
659 | { |
660 | if (out_child != NULL) |
661 | { |
662 | const char *name = g_file_info_get_name (info: ret_info); |
663 | |
664 | if (G_UNLIKELY (name == NULL)) |
665 | g_warning ("g_file_enumerator_iterate() created without standard::name" ); |
666 | else |
667 | { |
668 | *out_child = g_file_get_child (file: g_file_enumerator_get_container (enumerator: direnum), name); |
669 | g_object_set_qdata_full (object: (GObject*)direnum, quark: cached_child_quark, data: *out_child, destroy: (GDestroyNotify)g_object_unref); |
670 | } |
671 | } |
672 | if (out_info != NULL) |
673 | { |
674 | g_object_set_qdata_full (object: (GObject*)direnum, quark: cached_info_quark, data: ret_info, destroy: (GDestroyNotify)g_object_unref); |
675 | *out_info = ret_info; |
676 | } |
677 | else |
678 | g_object_unref (object: ret_info); |
679 | } |
680 | else |
681 | { |
682 | if (out_info) |
683 | *out_info = NULL; |
684 | if (out_child) |
685 | *out_child = NULL; |
686 | } |
687 | |
688 | ret = TRUE; |
689 | out: |
690 | return ret; |
691 | } |
692 | |
693 | /** |
694 | * g_file_enumerator_get_container: |
695 | * @enumerator: a #GFileEnumerator |
696 | * |
697 | * Get the #GFile container which is being enumerated. |
698 | * |
699 | * Returns: (transfer none): the #GFile which is being enumerated. |
700 | * |
701 | * Since: 2.18 |
702 | */ |
703 | GFile * |
704 | g_file_enumerator_get_container (GFileEnumerator *enumerator) |
705 | { |
706 | g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL); |
707 | |
708 | return enumerator->priv->container; |
709 | } |
710 | |
711 | /** |
712 | * g_file_enumerator_get_child: |
713 | * @enumerator: a #GFileEnumerator |
714 | * @info: a #GFileInfo gotten from g_file_enumerator_next_file() |
715 | * or the async equivalents. |
716 | * |
717 | * Return a new #GFile which refers to the file named by @info in the source |
718 | * directory of @enumerator. This function is primarily intended to be used |
719 | * inside loops with g_file_enumerator_next_file(). |
720 | * |
721 | * This is a convenience method that's equivalent to: |
722 | * |[<!-- language="C" --> |
723 | * gchar *name = g_file_info_get_name (info); |
724 | * GFile *child = g_file_get_child (g_file_enumerator_get_container (enumr), |
725 | * name); |
726 | * ]| |
727 | * |
728 | * Returns: (transfer full): a #GFile for the #GFileInfo passed it. |
729 | * |
730 | * Since: 2.36 |
731 | */ |
732 | GFile * |
733 | g_file_enumerator_get_child (GFileEnumerator *enumerator, |
734 | GFileInfo *info) |
735 | { |
736 | g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL); |
737 | |
738 | return g_file_get_child (file: enumerator->priv->container, |
739 | name: g_file_info_get_name (info)); |
740 | } |
741 | |
742 | static void |
743 | next_async_op_free (GList *files) |
744 | { |
745 | g_list_free_full (list: files, free_func: g_object_unref); |
746 | } |
747 | |
748 | static void |
749 | next_files_thread (GTask *task, |
750 | gpointer source_object, |
751 | gpointer task_data, |
752 | GCancellable *cancellable) |
753 | { |
754 | GFileEnumerator *enumerator = source_object; |
755 | int num_files = GPOINTER_TO_INT (task_data); |
756 | GFileEnumeratorClass *class; |
757 | GList *files = NULL; |
758 | GError *error = NULL; |
759 | GFileInfo *info; |
760 | int i; |
761 | |
762 | class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
763 | |
764 | for (i = 0; i < num_files; i++) |
765 | { |
766 | if (g_cancellable_set_error_if_cancelled (cancellable, error: &error)) |
767 | info = NULL; |
768 | else |
769 | info = class->next_file (enumerator, cancellable, &error); |
770 | |
771 | if (info == NULL) |
772 | { |
773 | /* If we get an error after first file, return that on next operation */ |
774 | if (error != NULL && i > 0) |
775 | { |
776 | if (g_error_matches (error, G_IO_ERROR, code: G_IO_ERROR_CANCELLED)) |
777 | g_error_free (error); /* Never propagate cancel errors to other call */ |
778 | else |
779 | enumerator->priv->outstanding_error = error; |
780 | error = NULL; |
781 | } |
782 | |
783 | break; |
784 | } |
785 | else |
786 | files = g_list_prepend (list: files, data: info); |
787 | } |
788 | |
789 | if (error) |
790 | g_task_return_error (task, error); |
791 | else |
792 | g_task_return_pointer (task, result: files, result_destroy: (GDestroyNotify)next_async_op_free); |
793 | } |
794 | |
795 | static void |
796 | g_file_enumerator_real_next_files_async (GFileEnumerator *enumerator, |
797 | int num_files, |
798 | int io_priority, |
799 | GCancellable *cancellable, |
800 | GAsyncReadyCallback callback, |
801 | gpointer user_data) |
802 | { |
803 | GTask *task; |
804 | |
805 | task = g_task_new (source_object: enumerator, cancellable, callback, callback_data: user_data); |
806 | g_task_set_source_tag (task, g_file_enumerator_real_next_files_async); |
807 | g_task_set_task_data (task, GINT_TO_POINTER (num_files), NULL); |
808 | g_task_set_priority (task, priority: io_priority); |
809 | |
810 | g_task_run_in_thread (task, task_func: next_files_thread); |
811 | g_object_unref (object: task); |
812 | } |
813 | |
814 | static GList * |
815 | g_file_enumerator_real_next_files_finish (GFileEnumerator *enumerator, |
816 | GAsyncResult *result, |
817 | GError **error) |
818 | { |
819 | g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL); |
820 | |
821 | return g_task_propagate_pointer (G_TASK (result), error); |
822 | } |
823 | |
824 | static void |
825 | close_async_thread (GTask *task, |
826 | gpointer source_object, |
827 | gpointer task_data, |
828 | GCancellable *cancellable) |
829 | { |
830 | GFileEnumerator *enumerator = source_object; |
831 | GFileEnumeratorClass *class; |
832 | GError *error = NULL; |
833 | gboolean result; |
834 | |
835 | class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
836 | result = class->close_fn (enumerator, cancellable, &error); |
837 | if (result) |
838 | g_task_return_boolean (task, TRUE); |
839 | else |
840 | g_task_return_error (task, error); |
841 | } |
842 | |
843 | static void |
844 | g_file_enumerator_real_close_async (GFileEnumerator *enumerator, |
845 | int io_priority, |
846 | GCancellable *cancellable, |
847 | GAsyncReadyCallback callback, |
848 | gpointer user_data) |
849 | { |
850 | GTask *task; |
851 | |
852 | task = g_task_new (source_object: enumerator, cancellable, callback, callback_data: user_data); |
853 | g_task_set_source_tag (task, g_file_enumerator_real_close_async); |
854 | g_task_set_priority (task, priority: io_priority); |
855 | |
856 | g_task_run_in_thread (task, task_func: close_async_thread); |
857 | g_object_unref (object: task); |
858 | } |
859 | |
860 | static gboolean |
861 | g_file_enumerator_real_close_finish (GFileEnumerator *enumerator, |
862 | GAsyncResult *result, |
863 | GError **error) |
864 | { |
865 | g_return_val_if_fail (g_task_is_valid (result, enumerator), FALSE); |
866 | |
867 | return g_task_propagate_boolean (G_TASK (result), error); |
868 | } |
869 | |