[glib] filenumerator: Add g_file_enumerator_iterate()
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] filenumerator: Add g_file_enumerator_iterate()
- Date: Fri, 20 Feb 2015 19:02:41 +0000 (UTC)
commit 52cd62d946946af3b0c9dac644ddc4bd517d131b
Author: Colin Walters <walters verbum org>
Date: Thu Feb 12 18:20:14 2015 -0500
filenumerator: Add g_file_enumerator_iterate()
This is *significantly* more pleasant to use from C (while handling
errors and memory cleanup).
While we're here, change some ugly, leaky code in
tests/desktop-app-info.c to use it, in addition to a test case
in tests/file.c.
https://bugzilla.gnome.org/show_bug.cgi?id=661554
docs/reference/gio/gio-sections.txt | 1 +
gio/gfileenumerator.c | 116 +++++++++++++++++++++++++++++++++++
gio/gfileenumerator.h | 8 +++
gio/tests/desktop-app-info.c | 77 +++++++++++++----------
gio/tests/file.c | 46 ++++++++++++++
5 files changed, 215 insertions(+), 33 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 5b691a4..757ccf2 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -220,6 +220,7 @@ g_file_get_type
<FILE>gfileenumerator</FILE>
<TITLE>GFileEnumerator</TITLE>
GFileEnumerator
+g_file_enumerator_iterate
g_file_enumerator_next_file
g_file_enumerator_close
g_file_enumerator_next_files_async
diff --git a/gio/gfileenumerator.c b/gio/gfileenumerator.c
index c0d4054..73014f2 100644
--- a/gio/gfileenumerator.c
+++ b/gio/gfileenumerator.c
@@ -574,6 +574,121 @@ g_file_enumerator_set_pending (GFileEnumerator *enumerator,
}
/**
+ * g_file_enumerator_iterate:
+ * @direnum: an open #GFileEnumerator
+ * @out_info: (out) (transfer none) (allow-none): Output location for the next #GFileInfo, or %NULL
+ * @out_child: (out) (transfer none) (allow-none): Output location for the next #GFile, or %NULL
+ * @cancellable: a #GCancellable
+ * @error: a #GError
+ *
+ * This is a version of g_file_enumerator_next_file() that's easier to
+ * use correctly from C programs. With g_file_enumerator_next_file(),
+ * the gboolean return value signifies "end of iteration or error", which
+ * requires allocation of a temporary #GError.
+ *
+ * In contrast, with this function, a %FALSE return from
+ * gs_file_enumerator_iterate() <emphasis>always</emphasis> means
+ * "error". End of iteration is signaled by @out_info or @out_child being %NULL.
+ *
+ * Another crucial difference is that the references for @out_info and
+ * @out_child are owned by @direnum (they are cached as hidden
+ * properties). You must not unref them in your own code. This makes
+ * memory management significantly easier for C code in combination
+ * with loops.
+ *
+ * Finally, this function optionally allows retrieving a #GFile as
+ * well.
+ *
+ * You must specify at least one of @out_info or @out_child.
+ *
+ * The code pattern for correctly using g_file_enumerator_iterate() from C
+ * is:
+ *
+ * |[
+ * direnum = g_file_enumerate_children (file, ...);
+ * while (TRUE)
+ * {
+ * GFileInfo *info;
+ * if (!g_file_enumerator_iterate (direnum, &info, NULL, cancellable, error))
+ * goto out;
+ * if (!info)
+ * break;
+ * ... do stuff with "info"; do not unref it! ...
+ * }
+ *
+ * out:
+ * g_object_unref (direnum); // Note: frees the last @info
+ * ]|
+ *
+ *
+ * Since: 2.44
+ */
+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_info != NULL)
+ {
+ g_object_set_qdata_full ((GObject*)direnum, cached_info_quark, ret_info,
(GDestroyNotify)g_object_unref);
+ *out_info = 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);
+ }
+ }
+ }
+ else
+ {
+ if (out_info)
+ *out_info = NULL;
+ if (out_child)
+ *out_child = NULL;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+/**
* g_file_enumerator_get_container:
* @enumerator: a #GFileEnumerator
*
@@ -747,3 +862,4 @@ g_file_enumerator_real_close_finish (GFileEnumerator *enumerator,
return g_task_propagate_boolean (G_TASK (result), error);
}
+
diff --git a/gio/gfileenumerator.h b/gio/gfileenumerator.h
index ceb62a5..fddcbe1 100644
--- a/gio/gfileenumerator.h
+++ b/gio/gfileenumerator.h
@@ -139,6 +139,14 @@ GLIB_AVAILABLE_IN_2_36
GFile * g_file_enumerator_get_child (GFileEnumerator *enumerator,
GFileInfo *info);
+GLIB_AVAILABLE_IN_2_44
+gboolean g_file_enumerator_iterate (GFileEnumerator *direnum,
+ GFileInfo **out_info,
+ GFile **out_child,
+ GCancellable *cancellable,
+ GError **error);
+
+
G_END_DECLS
#endif /* __G_FILE_ENUMERATOR_H__ */
diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c
index ccd6357..31ea215 100644
--- a/gio/tests/desktop-app-info.c
+++ b/gio/tests/desktop-app-info.c
@@ -268,64 +268,75 @@ test_last_used (void)
g_object_unref (default_app);
}
-static void
-cleanup_dir_recurse (GFile *parent, GFile *root)
+static gboolean
+cleanup_dir_recurse (GFile *parent,
+ GFile *root,
+ GError **error)
{
- gboolean res;
- GError *error;
+ gboolean ret = FALSE;
GFileEnumerator *enumerator;
- GFileInfo *info;
- GFile *descend;
- char *relative_path;
+ GError *local_error = NULL;
g_assert (root != NULL);
- error = NULL;
enumerator =
g_file_enumerate_children (parent, "*",
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL,
- &error);
- if (! enumerator)
- return;
- error = NULL;
- info = g_file_enumerator_next_file (enumerator, NULL, &error);
- while ((info) && (!error))
+ &local_error);
+ if (!enumerator)
{
- descend = g_file_get_child (parent, g_file_info_get_name (info));
- g_assert (descend != NULL);
- relative_path = g_file_get_relative_path (root, descend);
- g_assert (relative_path != NULL);
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_clear_error (&local_error);
+ ret = TRUE;
+ }
+ goto out;
+ }
- if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
- cleanup_dir_recurse (descend, root);
+ while (TRUE)
+ {
+ GFile *child;
+ GFileInfo *finfo;
+ char *relative_path;
- error = NULL;
- res = g_file_delete (descend, NULL, &error);
- g_assert_cmpint (res, ==, TRUE);
+ if (!g_file_enumerator_iterate (enumerator, &finfo, &child, NULL, error))
+ goto out;
+ if (!finfo)
+ break;
+
+ relative_path = g_file_get_relative_path (root, child);
+ g_assert (relative_path != NULL);
+ g_free (relative_path);
- g_object_unref (descend);
- error = NULL;
- info = g_file_enumerator_next_file (enumerator, NULL, &error);
+ if (g_file_info_get_file_type (finfo) == G_FILE_TYPE_DIRECTORY)
+ {
+ if (!cleanup_dir_recurse (child, root, error))
+ goto out;
+ }
+
+ if (!g_file_delete (child, NULL, error))
+ goto out;
}
- g_assert (error == NULL);
- error = NULL;
- res = g_file_enumerator_close (enumerator, NULL, &error);
- g_assert_cmpint (res, ==, TRUE);
- g_assert (error == NULL);
+ ret = TRUE;
+ out:
+ return ret;
}
static void
cleanup_subdirs (const char *base_dir)
{
GFile *base, *file;
+ GError *error = NULL;
base = g_file_new_for_path (base_dir);
file = g_file_get_child (base, "applications");
- cleanup_dir_recurse (file, file);
+ (void) cleanup_dir_recurse (file, file, &error);
+ g_assert_no_error (error);
g_object_unref (file);
file = g_file_get_child (base, "mime");
- cleanup_dir_recurse (file, file);
+ (void) cleanup_dir_recurse (file, file, &error);
+ g_assert_no_error (error);
g_object_unref (file);
}
diff --git a/gio/tests/file.c b/gio/tests/file.c
index cab1835..cba82d1 100644
--- a/gio/tests/file.c
+++ b/gio/tests/file.c
@@ -646,6 +646,7 @@ test_replace_cancel (void)
GCancellable *cancellable;
gchar *path;
gsize nwrote;
+ guint count;
GError *error = NULL;
g_test_bug ("629301");
@@ -691,6 +692,51 @@ test_replace_cancel (void)
g_assert_no_error (error);
g_object_unref (fenum);
+ /* Also test the g_file_enumerator_iterate() API */
+ fenum = g_file_enumerate_children (tmpdir, NULL, 0, NULL, &error);
+ g_assert_no_error (error);
+ count = 0;
+
+ while (TRUE)
+ {
+ gboolean ret = g_file_enumerator_iterate (fenum, &info, NULL, NULL, &error);
+ g_assert (ret);
+ g_assert_no_error (error);
+ if (!info)
+ break;
+ count++;
+ }
+ g_assert_cmpint (count, ==, 2);
+
+ g_file_enumerator_close (fenum, NULL, &error);
+ g_assert_no_error (error);
+ g_object_unref (fenum);
+
+ /* Now test just getting child from the g_file_enumerator_iterate() API */
+ fenum = g_file_enumerate_children (tmpdir, "standard::name", 0, NULL, &error);
+ g_assert_no_error (error);
+ count = 0;
+
+ while (TRUE)
+ {
+ GFile *child;
+ gboolean ret = g_file_enumerator_iterate (fenum, NULL, &child, NULL, &error);
+
+ g_assert (ret);
+ g_assert_no_error (error);
+
+ if (!child)
+ break;
+
+ g_assert (G_IS_FILE (child));
+ count++;
+ }
+ g_assert_cmpint (count, ==, 2);
+
+ g_file_enumerator_close (fenum, NULL, &error);
+ g_assert_no_error (error);
+ g_object_unref (fenum);
+
/* Make sure the temporary gets deleted even if we cancel. */
cancellable = g_cancellable_new ();
g_cancellable_cancel (cancellable);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]