| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include "config.h" |
| #include "gfileenumerator.h" |
| #include "gfile.h" |
| #include "gioscheduler.h" |
| #include "gasyncresult.h" |
| #include "gasynchelper.h" |
| #include "gioerror.h" |
| #include "glibintl.h" |
| |
| struct _GFileEnumeratorPrivate { |
| |
| GFile *container; |
| guint closed : 1; |
| guint pending : 1; |
| GAsyncReadyCallback outstanding_callback; |
| GError *outstanding_error; |
| }; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| G_DEFINE_TYPE_WITH_PRIVATE (GFileEnumerator, g_file_enumerator, G_TYPE_OBJECT) |
| |
| enum { |
| PROP_0, |
| PROP_CONTAINER |
| }; |
| |
| static void g_file_enumerator_real_next_files_async (GFileEnumerator *enumerator, |
| int num_files, |
| int io_priority, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data); |
| static GList * g_file_enumerator_real_next_files_finish (GFileEnumerator *enumerator, |
| GAsyncResult *res, |
| GError **error); |
| static void g_file_enumerator_real_close_async (GFileEnumerator *enumerator, |
| int io_priority, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data); |
| static gboolean g_file_enumerator_real_close_finish (GFileEnumerator *enumerator, |
| GAsyncResult *res, |
| GError **error); |
| |
| static void |
| g_file_enumerator_set_property (GObject *object, |
| guint property_id, |
| const GValue *value, |
| GParamSpec *pspec) |
| { |
| GFileEnumerator *enumerator; |
| |
| enumerator = G_FILE_ENUMERATOR (object); |
| |
| switch (property_id) { |
| case PROP_CONTAINER: |
| enumerator->priv->container = g_value_dup_object (value); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| g_file_enumerator_dispose (GObject *object) |
| { |
| GFileEnumerator *enumerator; |
| |
| enumerator = G_FILE_ENUMERATOR (object); |
| |
| if (enumerator->priv->container) { |
| g_object_unref (enumerator->priv->container); |
| enumerator->priv->container = NULL; |
| } |
| |
| G_OBJECT_CLASS (g_file_enumerator_parent_class)->dispose (object); |
| } |
| |
| static void |
| g_file_enumerator_finalize (GObject *object) |
| { |
| GFileEnumerator *enumerator; |
| |
| enumerator = G_FILE_ENUMERATOR (object); |
| |
| if (!enumerator->priv->closed) |
| g_file_enumerator_close (enumerator, NULL, NULL); |
| |
| G_OBJECT_CLASS (g_file_enumerator_parent_class)->finalize (object); |
| } |
| |
| static void |
| g_file_enumerator_class_init (GFileEnumeratorClass *klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| |
| gobject_class->set_property = g_file_enumerator_set_property; |
| gobject_class->dispose = g_file_enumerator_dispose; |
| gobject_class->finalize = g_file_enumerator_finalize; |
| |
| klass->next_files_async = g_file_enumerator_real_next_files_async; |
| klass->next_files_finish = g_file_enumerator_real_next_files_finish; |
| klass->close_async = g_file_enumerator_real_close_async; |
| klass->close_finish = g_file_enumerator_real_close_finish; |
| |
| g_object_class_install_property |
| (gobject_class, PROP_CONTAINER, |
| g_param_spec_object ("container", P_("Container"), |
| P_("The container that is being enumerated"), |
| G_TYPE_FILE, |
| G_PARAM_WRITABLE | |
| G_PARAM_CONSTRUCT_ONLY | |
| G_PARAM_STATIC_STRINGS)); |
| } |
| |
| static void |
| g_file_enumerator_init (GFileEnumerator *enumerator) |
| { |
| enumerator->priv = g_file_enumerator_get_instance_private (enumerator); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| GFileInfo * |
| g_file_enumerator_next_file (GFileEnumerator *enumerator, |
| GCancellable *cancellable, |
| GError **error) |
| { |
| GFileEnumeratorClass *class; |
| GFileInfo *info; |
| |
| g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL); |
| g_return_val_if_fail (enumerator != NULL, NULL); |
| |
| if (enumerator->priv->closed) |
| { |
| g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED, |
| _("Enumerator is closed")); |
| return NULL; |
| } |
| |
| if (enumerator->priv->pending) |
| { |
| g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PENDING, |
| _("File enumerator has outstanding operation")); |
| return NULL; |
| } |
| |
| if (enumerator->priv->outstanding_error) |
| { |
| g_propagate_error (error, enumerator->priv->outstanding_error); |
| enumerator->priv->outstanding_error = NULL; |
| return NULL; |
| } |
| |
| class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
| |
| if (cancellable) |
| g_cancellable_push_current (cancellable); |
| |
| enumerator->priv->pending = TRUE; |
| info = (* class->next_file) (enumerator, cancellable, error); |
| enumerator->priv->pending = FALSE; |
| |
| if (cancellable) |
| g_cancellable_pop_current (cancellable); |
| |
| return info; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| gboolean |
| g_file_enumerator_close (GFileEnumerator *enumerator, |
| GCancellable *cancellable, |
| GError **error) |
| { |
| GFileEnumeratorClass *class; |
| |
| g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), FALSE); |
| g_return_val_if_fail (enumerator != NULL, FALSE); |
| |
| class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
| |
| if (enumerator->priv->closed) |
| return TRUE; |
| |
| if (enumerator->priv->pending) |
| { |
| g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PENDING, |
| _("File enumerator has outstanding operation")); |
| return FALSE; |
| } |
| |
| if (cancellable) |
| g_cancellable_push_current (cancellable); |
| |
| enumerator->priv->pending = TRUE; |
| (* class->close_fn) (enumerator, cancellable, error); |
| enumerator->priv->pending = FALSE; |
| enumerator->priv->closed = TRUE; |
| |
| if (cancellable) |
| g_cancellable_pop_current (cancellable); |
| |
| return TRUE; |
| } |
| |
| static void |
| next_async_callback_wrapper (GObject *source_object, |
| GAsyncResult *res, |
| gpointer user_data) |
| { |
| GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source_object); |
| |
| enumerator->priv->pending = FALSE; |
| if (enumerator->priv->outstanding_callback) |
| (*enumerator->priv->outstanding_callback) (source_object, res, user_data); |
| g_object_unref (enumerator); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void |
| g_file_enumerator_next_files_async (GFileEnumerator *enumerator, |
| int num_files, |
| int io_priority, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| GFileEnumeratorClass *class; |
| |
| g_return_if_fail (G_IS_FILE_ENUMERATOR (enumerator)); |
| g_return_if_fail (enumerator != NULL); |
| g_return_if_fail (num_files >= 0); |
| |
| if (num_files == 0) |
| { |
| GTask *task; |
| |
| task = g_task_new (enumerator, cancellable, callback, user_data); |
| g_task_set_source_tag (task, g_file_enumerator_next_files_async); |
| g_task_return_pointer (task, NULL, NULL); |
| g_object_unref (task); |
| return; |
| } |
| |
| if (enumerator->priv->closed) |
| { |
| g_task_report_new_error (enumerator, callback, user_data, |
| g_file_enumerator_next_files_async, |
| G_IO_ERROR, G_IO_ERROR_CLOSED, |
| _("File enumerator is already closed")); |
| return; |
| } |
| |
| if (enumerator->priv->pending) |
| { |
| g_task_report_new_error (enumerator, callback, user_data, |
| g_file_enumerator_next_files_async, |
| G_IO_ERROR, G_IO_ERROR_PENDING, |
| _("File enumerator has outstanding operation")); |
| return; |
| } |
| |
| class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
| |
| enumerator->priv->pending = TRUE; |
| enumerator->priv->outstanding_callback = callback; |
| g_object_ref (enumerator); |
| (* class->next_files_async) (enumerator, num_files, io_priority, cancellable, |
| next_async_callback_wrapper, user_data); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| GList * |
| g_file_enumerator_next_files_finish (GFileEnumerator *enumerator, |
| GAsyncResult *result, |
| GError **error) |
| { |
| GFileEnumeratorClass *class; |
| |
| g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL); |
| g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL); |
| |
| if (g_async_result_legacy_propagate_error (result, error)) |
| return NULL; |
| else if (g_async_result_is_tagged (result, g_file_enumerator_next_files_async)) |
| return g_task_propagate_pointer (G_TASK (result), error); |
| |
| class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
| return class->next_files_finish (enumerator, result, error); |
| } |
| |
| static void |
| close_async_callback_wrapper (GObject *source_object, |
| GAsyncResult *res, |
| gpointer user_data) |
| { |
| GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source_object); |
| |
| enumerator->priv->pending = FALSE; |
| enumerator->priv->closed = TRUE; |
| if (enumerator->priv->outstanding_callback) |
| (*enumerator->priv->outstanding_callback) (source_object, res, user_data); |
| g_object_unref (enumerator); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void |
| g_file_enumerator_close_async (GFileEnumerator *enumerator, |
| int io_priority, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| GFileEnumeratorClass *class; |
| |
| g_return_if_fail (G_IS_FILE_ENUMERATOR (enumerator)); |
| |
| if (enumerator->priv->closed) |
| { |
| g_task_report_new_error (enumerator, callback, user_data, |
| g_file_enumerator_close_async, |
| G_IO_ERROR, G_IO_ERROR_CLOSED, |
| _("File enumerator is already closed")); |
| return; |
| } |
| |
| if (enumerator->priv->pending) |
| { |
| g_task_report_new_error (enumerator, callback, user_data, |
| g_file_enumerator_close_async, |
| G_IO_ERROR, G_IO_ERROR_PENDING, |
| _("File enumerator has outstanding operation")); |
| return; |
| } |
| |
| class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
| |
| enumerator->priv->pending = TRUE; |
| enumerator->priv->outstanding_callback = callback; |
| g_object_ref (enumerator); |
| (* class->close_async) (enumerator, io_priority, cancellable, |
| close_async_callback_wrapper, user_data); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| gboolean |
| g_file_enumerator_close_finish (GFileEnumerator *enumerator, |
| GAsyncResult *result, |
| GError **error) |
| { |
| GFileEnumeratorClass *class; |
| |
| g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), FALSE); |
| g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); |
| |
| if (g_async_result_legacy_propagate_error (result, error)) |
| return FALSE; |
| else if (g_async_result_is_tagged (result, g_file_enumerator_close_async)) |
| return g_task_propagate_boolean (G_TASK (result), error); |
| |
| class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
| return class->close_finish (enumerator, result, error); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| gboolean |
| g_file_enumerator_is_closed (GFileEnumerator *enumerator) |
| { |
| g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), TRUE); |
| |
| return enumerator->priv->closed; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| gboolean |
| g_file_enumerator_has_pending (GFileEnumerator *enumerator) |
| { |
| g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), TRUE); |
| |
| return enumerator->priv->pending; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| void |
| g_file_enumerator_set_pending (GFileEnumerator *enumerator, |
| gboolean pending) |
| { |
| g_return_if_fail (G_IS_FILE_ENUMERATOR (enumerator)); |
| |
| enumerator->priv->pending = pending; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| gboolean |
| g_file_enumerator_iterate (GFileEnumerator *direnum, |
| GFileInfo **out_info, |
| GFile **out_child, |
| GCancellable *cancellable, |
| GError **error) |
| { |
| gboolean ret = FALSE; |
| GError *temp_error = NULL; |
| GFileInfo *ret_info = NULL; |
| |
| static GQuark cached_info_quark; |
| static GQuark cached_child_quark; |
| static gsize quarks_initialized; |
| |
| g_return_val_if_fail (direnum != NULL, FALSE); |
| g_return_val_if_fail (out_info != NULL || out_child != NULL, FALSE); |
| |
| if (g_once_init_enter (&quarks_initialized)) |
| { |
| cached_info_quark = g_quark_from_static_string ("g-cached-info"); |
| cached_child_quark = g_quark_from_static_string ("g-cached-child"); |
| g_once_init_leave (&quarks_initialized, 1); |
| } |
| |
| ret_info = g_file_enumerator_next_file (direnum, cancellable, &temp_error); |
| if (temp_error != NULL) |
| { |
| g_propagate_error (error, temp_error); |
| goto out; |
| } |
| |
| if (ret_info) |
| { |
| if (out_child != NULL) |
| { |
| const char *name = g_file_info_get_name (ret_info); |
| |
| if (G_UNLIKELY (name == NULL)) |
| g_warning ("g_file_enumerator_iterate() created without standard::name"); |
| else |
| { |
| *out_child = g_file_get_child (g_file_enumerator_get_container (direnum), name); |
| g_object_set_qdata_full ((GObject*)direnum, cached_child_quark, *out_child, (GDestroyNotify)g_object_unref); |
| } |
| } |
| if (out_info != NULL) |
| { |
| g_object_set_qdata_full ((GObject*)direnum, cached_info_quark, ret_info, (GDestroyNotify)g_object_unref); |
| *out_info = ret_info; |
| } |
| else |
| g_object_unref (ret_info); |
| } |
| else |
| { |
| if (out_info) |
| *out_info = NULL; |
| if (out_child) |
| *out_child = NULL; |
| } |
| |
| ret = TRUE; |
| out: |
| return ret; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| GFile * |
| g_file_enumerator_get_container (GFileEnumerator *enumerator) |
| { |
| g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL); |
| |
| return enumerator->priv->container; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| GFile * |
| g_file_enumerator_get_child (GFileEnumerator *enumerator, |
| GFileInfo *info) |
| { |
| g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL); |
| |
| return g_file_get_child (enumerator->priv->container, |
| g_file_info_get_name (info)); |
| } |
| |
| static void |
| next_async_op_free (GList *files) |
| { |
| g_list_free_full (files, g_object_unref); |
| } |
| |
| static void |
| next_files_thread (GTask *task, |
| gpointer source_object, |
| gpointer task_data, |
| GCancellable *cancellable) |
| { |
| GFileEnumerator *enumerator = source_object; |
| int num_files = GPOINTER_TO_INT (task_data); |
| GFileEnumeratorClass *class; |
| GList *files = NULL; |
| GError *error = NULL; |
| GFileInfo *info; |
| int i; |
| |
| class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
| |
| for (i = 0; i < num_files; i++) |
| { |
| if (g_cancellable_set_error_if_cancelled (cancellable, &error)) |
| info = NULL; |
| else |
| info = class->next_file (enumerator, cancellable, &error); |
| |
| if (info == NULL) |
| { |
| |
| if (error != NULL && i > 0) |
| { |
| if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
| g_error_free (error); |
| else |
| enumerator->priv->outstanding_error = error; |
| error = NULL; |
| } |
| |
| break; |
| } |
| else |
| files = g_list_prepend (files, info); |
| } |
| |
| if (error) |
| g_task_return_error (task, error); |
| else |
| g_task_return_pointer (task, files, (GDestroyNotify)next_async_op_free); |
| } |
| |
| static void |
| g_file_enumerator_real_next_files_async (GFileEnumerator *enumerator, |
| int num_files, |
| int io_priority, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| GTask *task; |
| |
| task = g_task_new (enumerator, cancellable, callback, user_data); |
| g_task_set_source_tag (task, g_file_enumerator_real_next_files_async); |
| g_task_set_task_data (task, GINT_TO_POINTER (num_files), NULL); |
| g_task_set_priority (task, io_priority); |
| |
| g_task_run_in_thread (task, next_files_thread); |
| g_object_unref (task); |
| } |
| |
| static GList * |
| g_file_enumerator_real_next_files_finish (GFileEnumerator *enumerator, |
| GAsyncResult *result, |
| GError **error) |
| { |
| g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL); |
| |
| return g_task_propagate_pointer (G_TASK (result), error); |
| } |
| |
| static void |
| close_async_thread (GTask *task, |
| gpointer source_object, |
| gpointer task_data, |
| GCancellable *cancellable) |
| { |
| GFileEnumerator *enumerator = source_object; |
| GFileEnumeratorClass *class; |
| GError *error = NULL; |
| gboolean result; |
| |
| class = G_FILE_ENUMERATOR_GET_CLASS (enumerator); |
| result = class->close_fn (enumerator, cancellable, &error); |
| if (result) |
| g_task_return_boolean (task, TRUE); |
| else |
| g_task_return_error (task, error); |
| } |
| |
| static void |
| g_file_enumerator_real_close_async (GFileEnumerator *enumerator, |
| int io_priority, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| GTask *task; |
| |
| task = g_task_new (enumerator, cancellable, callback, user_data); |
| g_task_set_source_tag (task, g_file_enumerator_real_close_async); |
| g_task_set_priority (task, io_priority); |
| |
| g_task_run_in_thread (task, close_async_thread); |
| g_object_unref (task); |
| } |
| |
| static gboolean |
| g_file_enumerator_real_close_finish (GFileEnumerator *enumerator, |
| GAsyncResult *result, |
| GError **error) |
| { |
| g_return_val_if_fail (g_task_is_valid (result, enumerator), FALSE); |
| |
| return g_task_propagate_boolean (G_TASK (result), error); |
| } |
| |