mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-01 15:03:39 +02:00
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
This commit is contained in:
@@ -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
|
||||
|
@@ -573,6 +573,121 @@ g_file_enumerator_set_pending (GFileEnumerator *enumerator,
|
||||
enumerator->priv->pending = pending;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
@@ -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__ */
|
||||
|
@@ -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_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
|
||||
cleanup_dir_recurse (descend, root);
|
||||
|
||||
error = NULL;
|
||||
res = g_file_delete (descend, NULL, &error);
|
||||
g_assert_cmpint (res, ==, TRUE);
|
||||
|
||||
g_object_unref (descend);
|
||||
error = NULL;
|
||||
info = g_file_enumerator_next_file (enumerator, NULL, &error);
|
||||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
g_clear_error (&local_error);
|
||||
ret = TRUE;
|
||||
}
|
||||
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);
|
||||
while (TRUE)
|
||||
{
|
||||
GFile *child;
|
||||
GFileInfo *finfo;
|
||||
char *relative_path;
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user