* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
* (see #GInputStream and #GOutputStream).
*
* To construct a #GFile, you can use:
- * <simplelist>
- * <member>g_file_new_for_path() if you have a path.</member>
- * <member>g_file_new_for_uri() if you have a URI.</member>
- * <member>g_file_new_for_commandline_arg() for a command line argument.</member>
- * <member>g_file_new_tmp() to create a temporary file from a template.</member>
- * <member>g_file_parse_name() from a UTF-8 string gotten from g_file_get_parse_name().</member>
- * </simplelist>
+ * - g_file_new_for_path() if you have a path.
+ * - g_file_new_for_uri() if you have a URI.
+ * - g_file_new_for_commandline_arg() for a command line argument.
+ * - g_file_new_tmp() to create a temporary file from a template.
+ * - g_file_parse_name() from a UTF-8 string gotten from g_file_get_parse_name().
+ * - g_file_new_build_filename() to create a file from path elements.
*
* One way to think of a #GFile is as an abstraction of a pathname. For
* normal files the system pathname is what is stored internally, but as
* the operation, producing a GAsyncResult which is then passed to the
* function's matching _finish() operation.
*
- * Some #GFile operations do not have synchronous analogs, as they may
- * take a very long time to finish, and blocking may leave an application
- * unusable. Notable cases include:
- * <simplelist>
- * <member>g_file_mount_mountable() to mount a mountable file.</member>
- * <member>g_file_unmount_mountable_with_operation() to unmount a mountable file.</member>
- * <member>g_file_eject_mountable_with_operation() to eject a mountable file.</member>
- * </simplelist>
+ * It is highly recommended to use asynchronous calls when running within a
+ * shared main loop, such as in the main thread of an application. This avoids
+ * I/O operations blocking other sources on the main loop from being dispatched.
+ * Synchronous I/O operations should be performed from worker threads. See the
+ * [introduction to asynchronous programming section][async-programming] for
+ * more.
+ *
+ * Some #GFile operations almost always take a noticeable amount of time, and
+ * so do not have synchronous analogs. Notable cases include:
+ * - g_file_mount_mountable() to mount a mountable file.
+ * - g_file_unmount_mountable_with_operation() to unmount a mountable file.
+ * - g_file_eject_mountable_with_operation() to eject a mountable file.
+ *
+ * ## Entity Tags # {#gfile-etag}
*
- * <para id="gfile-etag"><indexterm><primary>entity tag</primary></indexterm>
* One notable feature of #GFiles are entity tags, or "etags" for
* short. Entity tags are somewhat like a more abstract version of the
- * traditional mtime, and can be used to quickly determine if the file has
- * been modified from the version on the file system. See the HTTP 1.1
- * <ulink url="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">specification</ulink>
+ * traditional mtime, and can be used to quickly determine if the file
+ * has been modified from the version on the file system. See the
+ * HTTP 1.1
+ * [specification](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html)
* for HTTP Etag headers, which are a very similar concept.
- * </para>
- **/
+ */
static void g_file_real_query_info_async (GFile *file,
const char *attributes,
*
* Checks to see if a file is native to the platform.
*
- * A native file s one expressed in the platform-native filename format,
+ * A native file is one expressed in the platform-native filename format,
* e.g. "C:\Windows" or "/usr/bin/". This does not mean the file is local,
* as it might be on a locally mounted remote filesystem.
*
*
* Gets the URI scheme for a #GFile.
* RFC 3986 decodes the scheme as:
- * <programlisting>
+ * |[
* URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
- * </programlisting>
+ * ]|
* Common schemes include "file", "http", "ftp", etc.
*
+ * The scheme can be different from the one used to construct the #GFile,
+ * in that it might be replaced with one that is logically equivalent to the #GFile.
+ *
* This call does no blocking I/O.
*
- * Returns: a string containing the URI scheme for the given
- * #GFile. The returned string should be freed with g_free()
- * when no longer needed.
+ * Returns: (nullable): a string containing the URI scheme for the given
+ * #GFile or %NULL if the #GFile was constructed with an invalid URI. The
+ * returned string should be freed with g_free() when no longer needed.
*/
char *
g_file_get_uri_scheme (GFile *file)
*
* This call does no blocking I/O.
*
- * Returns: string containing the #GFile's base name, or %NULL
- * if given #GFile is invalid. The returned string should be
- * freed with g_free() when no longer needed.
+ * Returns: (type filename) (nullable): string containing the #GFile's
+ * base name, or %NULL if given #GFile is invalid. The returned string
+ * should be freed with g_free() when no longer needed.
*/
char *
g_file_get_basename (GFile *file)
* g_file_get_path:
* @file: input #GFile
*
- * Gets the local pathname for #GFile, if one exists.
+ * Gets the local pathname for #GFile, if one exists. If non-%NULL, this is
+ * guaranteed to be an absolute, canonical path. It might contain symlinks.
*
* This call does no blocking I/O.
*
- * Returns: string containing the #GFile's path, or %NULL if
- * no such path exists. The returned string should be
- * freed with g_free() when no longer needed.
+ * Returns: (type filename) (nullable): string containing the #GFile's path,
+ * or %NULL if no such path exists. The returned string should be freed
+ * with g_free() when no longer needed.
*/
char *
g_file_get_path (GFile *file)
return (* iface->get_path) (file);
}
+static const char *
+file_peek_path_generic (GFile *file)
+{
+ const char *path;
+ static GQuark _file_path_quark = 0;
+
+ if (G_UNLIKELY (_file_path_quark) == 0)
+ _file_path_quark = g_quark_from_static_string ("gio-file-path");
+
+ /* We need to be careful about threading, as two threads calling
+ * g_file_peek_path() on the same file could race: both would see
+ * (g_object_get_qdata(…) == NULL) to begin with, both would generate and add
+ * the path, but the second thread to add it would end up freeing the path
+ * set by the first thread. The first thread would still return the pointer
+ * to that freed path, though, resulting an a read-after-free. Handle that
+ * with a compare-and-swap loop. The g_object_*_qdata() functions are atomic. */
+
+ while (TRUE)
+ {
+ gchar *new_path = NULL;
+
+ path = g_object_get_qdata ((GObject*)file, _file_path_quark);
+
+ if (path != NULL)
+ break;
+
+ new_path = g_file_get_path (file);
+ if (new_path == NULL)
+ return NULL;
+
+ /* By passing NULL here, we ensure we never replace existing data: */
+ if (g_object_replace_qdata ((GObject *) file, _file_path_quark,
+ NULL, (gpointer) new_path,
+ (GDestroyNotify) g_free, NULL))
+ {
+ path = new_path;
+ break;
+ }
+ else
+ g_free (new_path);
+ }
+
+ return path;
+}
+
+/**
+ * g_file_peek_path:
+ * @file: input #GFile
+ *
+ * Exactly like g_file_get_path(), but caches the result via
+ * g_object_set_qdata_full(). This is useful for example in C
+ * applications which mix `g_file_*` APIs with native ones. It
+ * also avoids an extra duplicated string when possible, so will be
+ * generally more efficient.
+ *
+ * This call does no blocking I/O.
+ *
+ * Returns: (type filename) (nullable): string containing the #GFile's path,
+ * or %NULL if no such path exists. The returned string is owned by @file.
+ * Since: 2.56
+ */
+const char *
+g_file_peek_path (GFile *file)
+{
+ if (G_IS_LOCAL_FILE (file))
+ return _g_local_file_get_filename ((GLocalFile *) file);
+ return file_peek_path_generic (file);
+}
+
/**
* g_file_get_uri:
* @file: input #GFile
*
* This call does no blocking I/O.
*
- * Returns: a string containing the #GFile's URI.
+ * Returns: a string containing the #GFile's URI. If the #GFile was constructed
+ * with an invalid URI, an invalid URI is returned.
* The returned string should be freed with g_free()
* when no longer needed.
*/
* the actual file or directory represented by the #GFile; see
* g_file_copy() if attempting to copy a file.
*
+ * g_file_dup() is useful when a second handle is needed to the same underlying
+ * file, for use in a separate thread (#GFile is not thread-safe). For use
+ * within the same thread, use g_object_ref() to increment the existing object’s
+ * reference count.
+ *
* This call does no blocking I/O.
*
* Returns: (transfer full): a new #GFile that is a duplicate
* @file1: the first #GFile
* @file2: the second #GFile
*
- * Checks equality of two given #GFiles.
+ * Checks if the two given #GFiles refer to the same file.
*
* Note that two #GFiles that differ can still refer to the same
* file on the filesystem due to various forms of filename
* This call does no blocking I/O.
*
* Returns: %TRUE if @file1 and @file2 are equal.
- * %FALSE if either is not a #GFile.
*/
gboolean
g_file_equal (GFile *file1,
g_return_val_if_fail (G_IS_FILE (file1), FALSE);
g_return_val_if_fail (G_IS_FILE (file2), FALSE);
+ if (file1 == file2)
+ return TRUE;
+
if (G_TYPE_FROM_INSTANCE (file1) != G_TYPE_FROM_INSTANCE (file2))
return FALSE;
*
* This call does no blocking I/O.
*
- * Returns: (transfer full): a #GFile structure to the
- * parent of the given #GFile or %NULL if there is
- * no parent. Free the returned object with g_object_unref().
+ * Returns: (nullable) (transfer full): a #GFile structure to the
+ * parent of the given #GFile or %NULL if there is no parent. Free
+ * the returned object with g_object_unref().
*/
GFile *
g_file_get_parent (GFile *file)
/**
* g_file_has_parent:
* @file: input #GFile
- * @parent: (allow-none): the parent to check for, or %NULL
+ * @parent: (nullable): the parent to check for, or %NULL
*
* Checks if @file has a parent, and optionally, if it is @parent.
*
* If @parent is %NULL then this function returns %TRUE if @file has any
* parent at all. If @parent is non-%NULL then %TRUE is only returned
- * if @file is a child of @parent.
+ * if @file is an immediate child of @parent.
*
- * Returns: %TRUE if @file is a child of @parent (or any parent in the
- * case that @parent is %NULL).
+ * Returns: %TRUE if @file is an immediate child of @parent (or any parent in
+ * the case that @parent is %NULL).
*
* Since: 2.24
*/
/**
* g_file_get_child:
* @file: input #GFile
- * @name: string containing the child's basename
+ * @name: (type filename): string containing the child's basename
*
* Gets a child of @file with basename equal to @name.
*
* so a path like /foo is not considered a prefix of /foobar, only
* of /foo/bar.
*
+ * A #GFile is not a prefix of itself. If you want to check for
+ * equality, use g_file_equal().
+ *
* This call does no I/O, as it works purely on names. As such it can
* sometimes return %FALSE even if @file is inside a @prefix (from a
* filesystem point of view), because the prefix of @file is an alias
* of @prefix.
*
* Virtual: prefix_matches
- * Returns: %TRUE if the @files's parent, grandparent, etc is @prefix,
+ * Returns: %TRUE if the @file's parent, grandparent, etc is @prefix,
* %FALSE otherwise.
*/
gboolean
*
* This call does no blocking I/O.
*
- * Returns: string with the relative path from @descendant
- * to @parent, or %NULL if @descendant doesn't have @parent
- * as prefix. The returned string should be freed with g_free()
- * when no longer needed.
+ * Returns: (type filename) (nullable): string with the relative path from
+ * @descendant to @parent, or %NULL if @descendant doesn't have @parent as
+ * prefix. The returned string should be freed with g_free() when
+ * no longer needed.
*/
char *
g_file_get_relative_path (GFile *parent,
/**
* g_file_resolve_relative_path:
* @file: input #GFile
- * @relative_path: a given relative path string
+ * @relative_path: (type filename): a given relative path string
*
* Resolves a relative path for @file to an absolute path.
*
* @file: input #GFile
* @attributes: an attribute query string
* @flags: a set of #GFileQueryInfoFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: #GError for error reporting
*
* @file: input #GFile
* @attributes: an attribute query string
* @flags: a set of #GFileQueryInfoFlags
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call when the
* request is satisfied
/**
* g_file_query_exists:
* @file: input #GFile
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
*
* Utility function to check if a particular file exists. This is
* implemented using g_file_query_info() and as such does blocking I/O.
*
- * Note that in many cases it is racy to first check for file existence
+ * Note that in many cases it is [racy to first check for file existence](https://en.wikipedia.org/wiki/Time_of_check_to_time_of_use)
* and then execute something based on the outcome of that, because the
* file might have been created or removed in between the operations. The
* general approach to handling that is to not check, but just do the
* g_file_query_file_type:
* @file: input #GFile
* @flags: a set of #GFileQueryInfoFlags passed to g_file_query_info()
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
*
* Utility function to inspect the #GFileType of a file. This is
* @file: input #GFile
* @attributes: an attribute query string
* @flags: a set of #GFileQueryInfoFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError
*
* @file: input #GFile
* @attributes: an attribute query string
* @flags: a set of #GFileQueryInfoFlags
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call when the
* request is satisfied
* g_file_query_filesystem_info:
* @file: input #GFile
* @attributes: an attribute query string
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError
*
* g_file_query_filesystem_info_async:
* @file: input #GFile
* @attributes: an attribute query string
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
* when the request is satisfied
/**
* g_file_find_enclosing_mount:
* @file: input #GFile
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError
*
* Gets a #GMount for the #GFile.
*
- * If the #GFileIface for @file does not have a mount (e.g.
- * possibly a remote share), @error will be set to %G_IO_ERROR_NOT_FOUND
- * and %NULL will be returned.
+ * #GMount is returned only for user interesting locations, see
+ * #GVolumeMonitor. If the #GFileIface for @file does not have a #mount,
+ * @error will be set to %G_IO_ERROR_NOT_FOUND and %NULL #will be returned.
*
* If @cancellable is not %NULL, then the operation can be cancelled by
* triggering the cancellable object from another thread. If the operation
/**
* g_file_find_enclosing_mount_async:
* @file: a #GFile
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
* when the request is satisfied
/**
* g_file_read:
* @file: #GFile to read
- * @cancellable: (allow-none): a #GCancellable
+ * @cancellable: (nullable): a #GCancellable
* @error: a #GError, or %NULL
*
* Opens a file for reading. The result is a #GFileInputStream that
* g_file_append_to:
* @file: input #GFile
* @flags: a set of #GFileCreateFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* g_file_create:
* @file: input #GFile
* @flags: a set of #GFileCreateFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
/**
* g_file_replace:
* @file: input #GFile
- * @etag: (allow-none): an optional <link linkend="gfile-etag">entity tag</link>
+ * @etag: (nullable): an optional [entity tag][gfile-etag]
* for the current #GFile, or #NULL to ignore
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* operation was cancelled, the error %G_IO_ERROR_CANCELLED will be
* returned.
*
- * If you pass in a non-%NULL @etag value, then this value is
- * compared to the current entity tag of the file, and if they differ
- * an %G_IO_ERROR_WRONG_ETAG error is returned. This generally means
- * that the file has been changed since you last read it. You can get
- * the new etag from g_file_output_stream_get_etag() after you've
- * finished writing and closed the #GFileOutputStream. When you load
- * a new file you can use g_file_input_stream_query_info() to get
- * the etag of the file.
+ * If you pass in a non-%NULL @etag value and @file already exists, then
+ * this value is compared to the current entity tag of the file, and if
+ * they differ an %G_IO_ERROR_WRONG_ETAG error is returned. This
+ * generally means that the file has been changed since you last read
+ * it. You can get the new etag from g_file_output_stream_get_etag()
+ * after you've finished writing and closed the #GFileOutputStream. When
+ * you load a new file you can use g_file_input_stream_query_info() to
+ * get the etag of the file.
*
* If @make_backup is %TRUE, this function will attempt to make a
* backup of the current file before overwriting it. If this fails
/**
* g_file_open_readwrite:
* @file: #GFile to open
- * @cancellable: (allow-none): a #GCancellable
+ * @cancellable: (nullable): a #GCancellable
* @error: a #GError, or %NULL
*
* Opens an existing file for reading and writing. The result is
* g_file_create_readwrite:
* @file: a #GFile
* @flags: a set of #GFileCreateFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: return location for a #GError, or %NULL
*
/**
* g_file_replace_readwrite:
* @file: a #GFile
- * @etag: (allow-none): an optional <link linkend="gfile-etag">entity tag</link>
+ * @etag: (nullable): an optional [entity tag][gfile-etag]
* for the current #GFile, or #NULL to ignore
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: return location for a #GError, or %NULL
*
/**
* g_file_read_async:
* @file: input #GFile
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
* when the request is satisfied
* g_file_append_to_async:
* @file: input #GFile
* @flags: a set of #GFileCreateFlags
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
* when the request is satisfied
* g_file_create_async:
* @file: input #GFile
* @flags: a set of #GFileCreateFlags
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
* when the request is satisfied
/**
* g_file_replace_async:
* @file: input #GFile
- * @etag: (allow-none): an <link linkend="gfile-etag">entity tag</link>
- * for the current #GFile, or NULL to ignore
+ * @etag: (nullable): an [entity tag][gfile-etag] for the current #GFile,
+ * or %NULL to ignore
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
* when the request is satisfied
/**
* g_file_open_readwrite_async
* @file: input #GFile
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
* when the request is satisfied
* g_file_create_readwrite_async:
* @file: input #GFile
* @flags: a set of #GFileCreateFlags
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
* when the request is satisfied
/**
* g_file_replace_readwrite_async:
* @file: input #GFile
- * @etag: (allow-none): an <link linkend="gfile-etag">entity tag</link>
- * for the current #GFile, or NULL to ignore
+ * @etag: (nullable): an [entity tag][gfile-etag] for the current #GFile,
+ * or %NULL to ignore
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
* when the request is satisfied
if (file_type == G_FILE_TYPE_DIRECTORY)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
- _("Can't copy over directory"));
+ _("Can’t copy over directory"));
return FALSE;
}
}
if (file_type == G_FILE_TYPE_DIRECTORY)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_MERGE,
- _("Can't copy directory over directory"));
+ _("Can’t copy directory over directory"));
return NULL;
}
/* continue to would_recurse error */
}
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
- _("Can't recursively copy directory"));
+ _("Can’t recursively copy directory"));
return NULL;
}
return info->flags & G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE;
}
-static gboolean
-build_attribute_list_for_copy (GFile *file,
- GFileCopyFlags flags,
- char **out_attributes,
- GCancellable *cancellable,
- GError **error)
+/**
+ * g_file_build_attribute_list_for_copy:
+ * @file: a #GFile to copy attributes to
+ * @flags: a set of #GFileCopyFlags
+ * @cancellable: (nullable): optional #GCancellable object,
+ * %NULL to ignore
+ * @error: a #GError, %NULL to ignore
+ *
+ * Prepares the file attribute query string for copying to @file.
+ *
+ * This function prepares an attribute query string to be
+ * passed to g_file_query_info() to get a list of attributes
+ * normally copied with the file (see g_file_copy_attributes()
+ * for the detailed description). This function is used by the
+ * implementation of g_file_copy_attributes() and is useful
+ * when one needs to query and set the attributes in two
+ * stages (e.g., for recursive move of a directory).
+ *
+ * Returns: an attribute query string for g_file_query_info(),
+ * or %NULL if an error occurs.
+ *
+ * Since: 2.68
+ */
+char *
+g_file_build_attribute_list_for_copy (GFile *file,
+ GFileCopyFlags flags,
+ GCancellable *cancellable,
+ GError **error)
{
- gboolean ret = FALSE;
+ char *ret = NULL;
GFileAttributeInfoList *attributes = NULL, *namespaces = NULL;
- GString *s;
+ GString *s = NULL;
gboolean first;
int i;
gboolean copy_all_attributes;
gboolean skip_perms;
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
copy_all_attributes = flags & G_FILE_COPY_ALL_METADATA;
skip_perms = (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS) != 0;
}
}
- ret = TRUE;
- *out_attributes = g_string_free (s, FALSE);
+ ret = g_string_free (s, FALSE);
s = NULL;
out:
if (s)
* @source: a #GFile with attributes
* @destination: a #GFile to copy attributes to
* @flags: a set of #GFileCopyFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, %NULL to ignore
*
GFileInfo *info;
gboolean source_nofollow_symlinks;
- if (!build_attribute_list_for_copy (destination, flags, &attrs_to_read,
- cancellable, error))
+ attrs_to_read = g_file_build_attribute_list_for_copy (destination, flags,
+ cancellable, error);
+ if (!attrs_to_read)
return FALSE;
source_nofollow_symlinks = flags & G_FILE_COPY_NOFOLLOW_SYMLINKS;
return res;
}
+/* 256k minus malloc overhead */
+#define STREAM_BUFFER_SIZE (1024*256 - 2 *sizeof(gpointer))
+
static gboolean
copy_stream_with_progress (GInputStream *in,
GOutputStream *out,
gpointer progress_callback_data,
GError **error)
{
- gssize n_read, n_written;
+ gssize n_read;
+ gsize n_written;
goffset current_size;
- char buffer[1024*64], *p;
+ char *buffer;
gboolean res;
goffset total_size;
GFileInfo *info;
if (total_size == -1)
total_size = 0;
+ buffer = g_malloc0 (STREAM_BUFFER_SIZE);
current_size = 0;
res = TRUE;
while (TRUE)
{
- n_read = g_input_stream_read (in, buffer, sizeof (buffer), cancellable, error);
+ n_read = g_input_stream_read (in, buffer, STREAM_BUFFER_SIZE, cancellable, error);
if (n_read == -1)
{
res = FALSE;
current_size += n_read;
- p = buffer;
- while (n_read > 0)
- {
- n_written = g_output_stream_write (out, p, n_read, cancellable, error);
- if (n_written == -1)
- {
- res = FALSE;
- break;
- }
-
- p += n_written;
- n_read -= n_written;
- }
-
+ res = g_output_stream_write_all (out, buffer, n_read, &n_written, cancellable, error);
if (!res)
break;
if (progress_callback)
progress_callback (current_size, total_size, progress_callback_data);
}
+ g_free (buffer);
/* Make sure we send full copied size */
if (progress_callback)
if (errsv == EINTR)
goto retry;
- else if (errsv == ENOSYS || errsv == EINVAL)
+ else if (errsv == ENOSYS || errsv == EINVAL || errsv == EOPNOTSUPP)
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Splice not supported"));
else
GError **error)
{
int buffer[2] = { -1, -1 };
+ int buffer_size;
gboolean res;
goffset total_size;
loff_t offset_in;
if (!g_unix_open_pipe (buffer, FD_CLOEXEC, error))
return FALSE;
+#if defined(F_SETPIPE_SZ) && defined(F_GETPIPE_SZ)
+ /* Try a 1MiB buffer for improved throughput. If that fails, use the default
+ * pipe size. See: https://bugzilla.gnome.org/791457 */
+ buffer_size = fcntl (buffer[1], F_SETPIPE_SZ, 1024 * 1024);
+ if (buffer_size <= 0)
+ {
+ int errsv;
+ buffer_size = fcntl (buffer[1], F_GETPIPE_SZ);
+ errsv = errno;
+
+ if (buffer_size <= 0)
+ {
+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+ _("Error splicing file: %s"), g_strerror (errsv));
+ res = FALSE;
+ goto out;
+ }
+ }
+#else
+ /* If #F_GETPIPE_SZ isn’t available, assume we’re on Linux < 2.6.35,
+ * but ≥ 2.6.11, meaning the pipe capacity is 64KiB. Ignore the possibility of
+ * running on Linux < 2.6.11 (where the capacity was the system page size,
+ * typically 4KiB) because it’s ancient. See pipe(7). */
+ buffer_size = 1024 * 64;
+#endif
+
+ g_assert (buffer_size > 0);
+
total_size = -1;
/* avoid performance impact of querying total size when it's not needed */
if (progress_callback)
if (g_cancellable_set_error_if_cancelled (cancellable, error))
break;
- if (!do_splice (fd_in, &offset_in, buffer[1], NULL, 1024*64, &n_read, error))
+ if (!do_splice (fd_in, &offset_in, buffer[1], NULL, buffer_size, &n_read, error))
break;
if (n_read == 0)
{
goffset source_size;
int fd_in, fd_out;
- int ret;
+ int ret, errsv;
fd_in = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (in));
fd_out = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (out));
*
* By the time we get here, *in and *out are both regular files */
ret = ioctl (fd_out, BTRFS_IOC_CLONE, fd_in);
+ errsv = errno;
if (ret < 0)
{
- if (errno == EXDEV)
+ if (errsv == EXDEV)
g_set_error_literal (error, G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("Copy (reflink/clone) between mounts is not supported"));
- else if (errno == EINVAL)
+ else if (errsv == EINVAL)
g_set_error_literal (error, G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("Copy (reflink/clone) is not supported or invalid"));
/* Most probably something odd happened; retry with fallback */
g_set_error_literal (error, G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
- _("Copy (reflink/clone) is not supported or didn't work"));
+ _("Copy (reflink/clone) is not supported or didn’t work"));
/* We retry with fallback for all error cases because Btrfs is currently
* unstable, and so we can't trust it to do clone properly.
* In addition, any hard errors here would cause the same failure in the
const char *target;
char *attrs_to_read;
gboolean do_set_attributes = FALSE;
+ GFileCreateFlags create_flags;
+ GError *tmp_error = NULL;
/* need to know the file type */
info = g_file_query_info (source,
{
/* FIXME: could try to recreate device nodes and others? */
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("Can't copy special file"));
+ _("Can’t copy special file"));
goto out;
}
goto out;
in = G_INPUT_STREAM (file_in);
- if (!build_attribute_list_for_copy (destination, flags, &attrs_to_read,
- cancellable, error))
+ attrs_to_read = g_file_build_attribute_list_for_copy (destination, flags,
+ cancellable, error);
+ if (!attrs_to_read)
goto out;
- if (attrs_to_read != NULL)
- {
- GError *tmp_error = NULL;
+ /* Ok, ditch the previous lightweight info (on Unix we just
+ * called lstat()); at this point we gather all the information
+ * we need about the source from the opened file descriptor.
+ */
+ g_object_unref (info);
- /* Ok, ditch the previous lightweight info (on Unix we just
- * called lstat()); at this point we gather all the information
- * we need about the source from the opened file descriptor.
+ info = g_file_input_stream_query_info (file_in, attrs_to_read,
+ cancellable, &tmp_error);
+ if (!info)
+ {
+ /* Not all gvfs backends implement query_info_on_read(), we
+ * can just fall back to the pathname again.
+ * https://bugzilla.gnome.org/706254
*/
- g_object_unref (info);
-
- info = g_file_input_stream_query_info (file_in, attrs_to_read,
- cancellable, &tmp_error);
- if (!info)
+ if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
{
- /* Not all gvfs backends implement query_info_on_read(), we
- * can just fall back to the pathname again.
- * https://bugzilla.gnome.org/706254
- */
- if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
- {
- g_clear_error (&tmp_error);
- info = g_file_query_info (source, attrs_to_read, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- cancellable, error);
- }
- else
- {
- g_free (attrs_to_read);
- g_propagate_error (error, tmp_error);
- goto out;
- }
+ g_clear_error (&tmp_error);
+ info = g_file_query_info (source, attrs_to_read, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable, error);
+ }
+ else
+ {
+ g_free (attrs_to_read);
+ g_propagate_error (error, tmp_error);
+ goto out;
}
- g_free (attrs_to_read);
- if (!info)
- goto out;
-
- do_set_attributes = TRUE;
}
+ g_free (attrs_to_read);
+ if (!info)
+ goto out;
+
+ do_set_attributes = TRUE;
/* In the local file path, we pass down the source info which
* includes things like unix::mode, to ensure that the target file
*
* If a future API like g_file_replace_with_info() is added, switch
* this code to use that.
+ *
+ * Use %G_FILE_CREATE_PRIVATE unless
+ * - we were told to create the file with default permissions (i.e. the
+ * process’ umask),
+ * - or if the source file is on a file system which doesn’t support
+ * `unix::mode` (in which case it probably also makes sense to create the
+ * destination with default permissions because the source cannot be
+ * private),
+ * - or if the destination file is a `GLocalFile`, in which case we can
+ * directly open() it with the permissions from the source file.
*/
+ create_flags = G_FILE_CREATE_NONE;
+ if (!(flags & G_FILE_COPY_TARGET_DEFAULT_PERMS) &&
+ g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_MODE) &&
+ !G_IS_LOCAL_FILE (destination))
+ create_flags |= G_FILE_CREATE_PRIVATE;
+ if (flags & G_FILE_COPY_OVERWRITE)
+ create_flags |= G_FILE_CREATE_REPLACE_DESTINATION;
+
if (G_IS_LOCAL_FILE (destination))
{
if (flags & G_FILE_COPY_OVERWRITE)
out = (GOutputStream*)_g_local_file_output_stream_replace (_g_local_file_get_filename (G_LOCAL_FILE (destination)),
FALSE, NULL,
flags & G_FILE_COPY_BACKUP,
- G_FILE_CREATE_REPLACE_DESTINATION,
- info,
+ create_flags,
+ (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS) ? NULL : info,
cancellable, error);
else
out = (GOutputStream*)_g_local_file_output_stream_create (_g_local_file_get_filename (G_LOCAL_FILE (destination)),
- FALSE, 0, info,
+ FALSE, create_flags,
+ (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS) ? NULL : info,
cancellable, error);
}
else if (flags & G_FILE_COPY_OVERWRITE)
out = (GOutputStream *)g_file_replace (destination,
NULL,
flags & G_FILE_COPY_BACKUP,
- G_FILE_CREATE_REPLACE_DESTINATION,
+ create_flags,
cancellable, error);
}
else
{
- out = (GOutputStream *)g_file_create (destination, 0, cancellable, error);
+ out = (GOutputStream *)g_file_create (destination, create_flags, cancellable, error);
}
if (!out)
info,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable,
- error);
+ NULL);
}
g_clear_object (&info);
* @source: input #GFile
* @destination: destination #GFile
* @flags: set of #GFileCopyFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
- * @progress_callback: (allow-none) (scope call): function to callback with
+ * @progress_callback: (nullable) (scope call): function to callback with
* progress information, or %NULL if progress information is not needed
* @progress_callback_data: (closure): user data to pass to @progress_callback
* @error: #GError to set on error, or %NULL
* will be copied as symlinks, otherwise the target of the
* @source symlink will be copied.
*
+ * If the flag #G_FILE_COPY_ALL_METADATA is specified then all the metadata
+ * that is possible to copy is copied, not just the default subset (which,
+ * for instance, does not include the owner, see #GFileInfo).
+ *
* If @cancellable is not %NULL, then the operation can be cancelled by
* triggering the cancellable object from another thread. If the operation
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
}
/**
- * g_file_copy_async: (skip)
+ * g_file_copy_async:
* @source: input #GFile
* @destination: destination #GFile
* @flags: set of #GFileCopyFlags
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
- * @progress_callback: (allow-none): function to callback with progress
+ * @progress_callback: (nullable) (scope notified): function to callback with progress
* information, or %NULL if progress information is not needed
- * @progress_callback_data: (closure): user data to pass to @progress_callback
- * @callback: a #GAsyncReadyCallback to call when the request is satisfied
- * @user_data: the data to pass to callback function
+ * @progress_callback_data: (closure progress_callback) (nullable): user data to pass to @progress_callback
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: (closure callback): the data to pass to callback function
*
* Copies the file @source to the location specified by @destination
* asynchronously. For details of the behaviour, see g_file_copy().
*
* If @progress_callback is not %NULL, then that function that will be called
- * just like in g_file_copy(), however the callback will run in the main loop,
- * not in the thread that is doing the I/O operation.
+ * just like in g_file_copy(). The callback will run in the default main context
+ * of the thread calling g_file_copy_async() — the same context as @callback is
+ * run in.
*
* When the operation is finished, @callback will be called. You can then call
* g_file_copy_finish() to get the result of the operation.
* @source: #GFile pointing to the source location
* @destination: #GFile pointing to the destination location
* @flags: set of #GFileCopyFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
- * @progress_callback: (allow-none) (scope call): #GFileProgressCallback
+ * @progress_callback: (nullable) (scope call): #GFileProgressCallback
* function for updates
* @progress_callback_data: (closure): gpointer to user data for
* the callback function
* If the flag #G_FILE_COPY_OVERWRITE is specified an already
* existing @destination file is overwritten.
*
- * If the flag #G_FILE_COPY_NOFOLLOW_SYMLINKS is specified then symlinks
- * will be copied as symlinks, otherwise the target of the
- * @source symlink will be copied.
- *
* If @cancellable is not %NULL, then the operation can be cancelled by
* triggering the cancellable object from another thread. If the operation
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
g_propagate_error (error, my_error);
return FALSE;
}
+ else
+ g_clear_error (&my_error);
}
/* If the types are different, and the destination method failed
g_propagate_error (error, my_error);
return FALSE;
}
+ else
+ g_clear_error (&my_error);
}
}
return FALSE;
}
- flags |= G_FILE_COPY_ALL_METADATA;
+ flags |= G_FILE_COPY_ALL_METADATA | G_FILE_COPY_NOFOLLOW_SYMLINKS;
if (!g_file_copy (source, destination, flags, cancellable,
progress_callback, progress_callback_data,
error))
/**
* g_file_make_directory:
* @file: input #GFile
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
/**
* g_file_make_directory_async:
* @file: input #GFile
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: a #GAsyncReadyCallback to call
* when the request is satisfied
/**
* g_file_make_directory_with_parents:
* @file: input #GFile
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE;
+ /* Try for the simple case of not having to create any parent
+ * directories. If any parent directory needs to be created, this
+ * call will fail with NOT_FOUND. If that happens, then that value of
+ * my_error persists into the while loop below.
+ */
g_file_make_directory (file, cancellable, &my_error);
- if (my_error == NULL || my_error->code != G_IO_ERROR_NOT_FOUND)
+ if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
if (my_error)
g_propagate_error (error, my_error);
work_file = g_object_ref (file);
- while (my_error != NULL && my_error->code == G_IO_ERROR_NOT_FOUND)
+ /* Creates the parent directories as needed. In case any particular
+ * creation operation fails for lack of other parent directories
+ * (NOT_FOUND), the directory is added to a list of directories to
+ * create later, and the value of my_error is retained until the next
+ * iteration of the loop. After the loop my_error should either be
+ * empty or contain a real failure condition.
+ */
+ while (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
GFile *parent_file;
g_clear_error (&my_error);
g_file_make_directory (parent_file, cancellable, &my_error);
+ /* Another process may have created the directory in between the
+ * G_IO_ERROR_NOT_FOUND and now
+ */
+ if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ g_clear_error (&my_error);
g_object_unref (work_file);
work_file = g_object_ref (parent_file);
- if (my_error != NULL && my_error->code == G_IO_ERROR_NOT_FOUND)
+ if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
list = g_list_prepend (list, parent_file); /* Transfer ownership of ref */
else
g_object_unref (parent_file);
}
+ /* All directories should be able to be created now, so an error at
+ * this point means the whole operation must fail -- except an EXISTS
+ * error, which means that another process already created the
+ * directory in between the previous failure and now.
+ */
for (l = list; my_error == NULL && l; l = l->next)
{
g_file_make_directory ((GFile *) l->data, cancellable, &my_error);
+ if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ g_clear_error (&my_error);
}
if (work_file)
list = g_list_remove (list, list->data);
}
+ /* At this point an error in my_error means a that something
+ * unexpected failed in either of the loops above, so the whole
+ * operation must fail.
+ */
if (my_error != NULL)
{
g_propagate_error (error, my_error);
/**
* g_file_make_symbolic_link:
* @file: a #GFile with the name of the symlink to create
- * @symlink_value: a string with the path for the target of the new symlink
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @symlink_value: (type filename): a string with the path for the target
+ * of the new symlink
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError
*
{
g_set_error_literal (error, G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
- _("Operation not supported"));
+ _("Symbolic links not supported"));
return FALSE;
}
/**
* g_file_delete:
* @file: input #GFile
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* Deletes a file. If the @file is a directory, it will only be
* deleted if it is empty. This has the same semantics as g_unlink().
*
+ * If @file doesn’t exist, %G_IO_ERROR_NOT_FOUND will be returned. This allows
+ * for deletion to be implemented avoiding
+ * [time-of-check to time-of-use races](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use):
+ * |[
+ * g_autoptr(GError) local_error = NULL;
+ * if (!g_file_delete (my_file, my_cancellable, &local_error) &&
+ * !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ * {
+ * // deletion failed for some reason other than the file not existing:
+ * // so report the error
+ * g_warning ("Failed to delete %s: %s",
+ * g_file_peek_path (my_file), local_error->message);
+ * }
+ * ]|
+ *
* If @cancellable is not %NULL, then the operation can be cancelled by
* triggering the cancellable object from another thread. If the operation
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
/**
* g_file_delete_async:
* @file: input #GFile
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: a #GAsyncReadyCallback to call
* when the request is satisfied
/**
* g_file_trash:
* @file: #GFile to send to trash
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* Sends @file to the "Trashcan", if possible. This is similar to
* deleting it, but the user can recover it before emptying the trashcan.
* Not all file systems support trashing, so this call can return the
- * %G_IO_ERROR_NOT_SUPPORTED error.
+ * %G_IO_ERROR_NOT_SUPPORTED error. Since GLib 2.66, the `x-gvfs-notrash` unix
+ * mount option can be used to disable g_file_trash() support for certain
+ * mounts, the %G_IO_ERROR_NOT_SUPPORTED error will be returned in that case.
*
* If @cancellable is not %NULL, then the operation can be cancelled by
* triggering the cancellable object from another thread. If the operation
/**
* g_file_trash_async:
* @file: input #GFile
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: a #GAsyncReadyCallback to call
* when the request is satisfied
* g_file_set_display_name:
* @file: input #GFile
* @display_name: a string
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
- _("File names cannot contain '%c'"), G_DIR_SEPARATOR);
+ _("File names cannot contain “%c”"), G_DIR_SEPARATOR);
return NULL;
}
* g_file_set_display_name_async:
* @file: input #GFile
* @display_name: a string
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
* when the request is satisfied
/**
* g_file_query_settable_attributes:
* @file: input #GFile
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
/**
* g_file_query_writable_namespaces:
* @file: input #GFile
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
if (list == NULL)
{
+ g_warn_if_reached();
+ list = g_file_attribute_info_list_new ();
+ }
+
+ if (my_error != NULL)
+ {
if (my_error->domain == G_IO_ERROR && my_error->code == G_IO_ERROR_NOT_SUPPORTED)
{
- list = g_file_attribute_info_list_new ();
g_error_free (my_error);
}
else
* @file: input #GFile
* @attribute: a string containing the attribute's name
* @type: The type of the attribute
- * @value_p: (allow-none): a pointer to the value (or the pointer
+ * @value_p: (nullable): a pointer to the value (or the pointer
* itself if the type is a pointer type)
* @flags: a set of #GFileQueryInfoFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
- * Sets an attribute in the file with attribute name @attribute to @value.
+ * Sets an attribute in the file with attribute name @attribute to @value_p.
*
- * Some attributes can be unset by setting @attribute to
+ * Some attributes can be unset by setting @type to
* %G_FILE_ATTRIBUTE_TYPE_INVALID and @value_p to %NULL.
*
* If @cancellable is not %NULL, then the operation can be cancelled by
* @file: input #GFile
* @info: a #GFileInfo
* @flags: #GFileQueryInfoFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* @file: input #GFile
* @info: a #GFileInfo
* @flags: a #GFileQueryInfoFlags
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback
* @user_data: (closure): a #gpointer
* @attribute: a string containing the attribute's name
* @value: a string containing the attribute's value
* @flags: #GFileQueryInfoFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* @attribute: a string containing the attribute's name
* @value: a string containing the attribute's new value
* @flags: a #GFileQueryInfoFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* @attribute: a string containing the attribute's name
* @value: a #guint32 containing the attribute's new value
* @flags: a #GFileQueryInfoFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* @attribute: a string containing the attribute's name
* @value: a #gint32 containing the attribute's new value
* @flags: a #GFileQueryInfoFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* @attribute: a string containing the attribute's name
* @value: a #guint64 containing the attribute's new value
* @flags: a #GFileQueryInfoFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* @attribute: a string containing the attribute's name
* @value: a #guint64 containing the attribute's new value
* @flags: a #GFileQueryInfoFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* g_file_mount_mountable:
* @file: input #GFile
* @flags: flags affecting the operation
- * @mount_operation: (allow-none): a #GMountOperation,
+ * @mount_operation: (nullable): a #GMountOperation,
* or %NULL to avoid user interaction
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
- * @callback: (scope async) (allow-none): a #GAsyncReadyCallback to call
+ * @callback: (scope async) (nullable): a #GAsyncReadyCallback to call
* when the request is satisfied, or %NULL
* @user_data: (closure): the data to pass to callback function
*
* g_file_unmount_mountable:
* @file: input #GFile
* @flags: flags affecting the operation
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
- * @callback: (scope async) (allow-none): a #GAsyncReadyCallback to call
+ * @callback: (scope async) (nullable): a #GAsyncReadyCallback to call
* when the request is satisfied, or %NULL
* @user_data: (closure): the data to pass to callback function
*
* g_file_unmount_mountable_with_operation:
* @file: input #GFile
* @flags: flags affecting the operation
- * @mount_operation: (allow-none): a #GMountOperation,
+ * @mount_operation: (nullable): a #GMountOperation,
* or %NULL to avoid user interaction
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
- * @callback: (scope async) (allow-none): a #GAsyncReadyCallback to call
+ * @callback: (scope async) (nullable): a #GAsyncReadyCallback to call
* when the request is satisfied, or %NULL
* @user_data: (closure): the data to pass to callback function
*
* g_file_eject_mountable:
* @file: input #GFile
* @flags: flags affecting the operation
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
- * @callback: (scope async) (allow-none): a #GAsyncReadyCallback to call
+ * @callback: (scope async) (nullable): a #GAsyncReadyCallback to call
* when the request is satisfied, or %NULL
* @user_data: (closure): the data to pass to callback function
*
* g_file_eject_mountable_with_operation:
* @file: input #GFile
* @flags: flags affecting the operation
- * @mount_operation: (allow-none): a #GMountOperation,
+ * @mount_operation: (nullable): a #GMountOperation,
* or %NULL to avoid user interaction
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
- * @callback: (scope async) (allow-none): a #GAsyncReadyCallback to call
+ * @callback: (scope async) (nullable): a #GAsyncReadyCallback to call
* when the request is satisfied, or %NULL
* @user_data: (closure): the data to pass to callback function
*
* g_file_monitor_directory:
* @file: input #GFile
* @flags: a set of #GFileMonitorFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* g_file_monitor_file:
* @file: input #GFile
* @flags: a set of #GFileMonitorFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
* g_file_monitor:
* @file: input #GFile
* @flags: a set of #GFileMonitorFlags
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
* @error: a #GError, or %NULL
*
data->flags = flags;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_query_info_async);
g_task_set_task_data (task, data, (GDestroyNotify)query_info_data_free);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, query_info_async_thread);
GTask *task;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_query_filesystem_info_async);
g_task_set_task_data (task, g_strdup (attributes), g_free);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, query_filesystem_info_async_thread);
data->flags = flags;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_enumerate_children_async);
g_task_set_task_data (task, data, (GDestroyNotify)query_info_data_free);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, enumerate_children_async_thread);
GTask *task;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_read_async);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, open_read_async_thread);
g_object_unref (task);
*data = flags;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_append_to_async);
g_task_set_task_data (task, data, g_free);
g_task_set_priority (task, io_priority);
*data = flags;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_create_async);
g_task_set_task_data (task, data, g_free);
g_task_set_priority (task, io_priority);
data->flags = flags;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_replace_async);
g_task_set_task_data (task, data, (GDestroyNotify)replace_async_data_free);
g_task_set_priority (task, io_priority);
GTask *task;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_delete_async);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, delete_async_thread);
g_object_unref (task);
GTask *task;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_trash_async);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, trash_async_thread);
g_object_unref (task);
GTask *task;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_make_directory_async);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, make_directory_async_thread);
g_object_unref (task);
GTask *task;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_open_readwrite_async);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, open_readwrite_async_thread);
*data = flags;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_create_readwrite_async);
g_task_set_task_data (task, data, g_free);
g_task_set_priority (task, io_priority);
data->flags = flags;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_replace_readwrite_async);
g_task_set_task_data (task, data, (GDestroyNotify)replace_rw_async_data_free);
g_task_set_priority (task, io_priority);
GTask *task;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_set_display_name_async);
g_task_set_task_data (task, g_strdup (display_name), g_free);
g_task_set_priority (task, io_priority);
data->flags = flags;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_set_attributes_async);
g_task_set_task_data (task, data, (GDestroyNotify)set_info_data_free);
g_task_set_priority (task, io_priority);
GTask *task;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_find_enclosing_mount_async);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, find_enclosing_mount_async_thread);
data->progress_cb_data = progress_callback_data;
task = g_task_new (source, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_copy_async);
g_task_set_task_data (task, data, (GDestroyNotify)copy_async_data_free);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, copy_async_thread);
/**
* g_file_new_for_path:
- * @path: a string containing a relative or absolute path.
+ * @path: (type filename): a string containing a relative or absolute path.
* The string must be encoded in the glib filename encoding.
*
* Constructs a #GFile for a given path. This operation never
/**
* g_file_new_tmp:
- * @tmpl: (type filename) (allow-none): Template for the file
+ * @tmpl: (type filename) (nullable): Template for the file
* name, as in g_file_open_tmp(), or %NULL for a default template
* @iostream: (out): on return, a #GFileIOStream for the created file
* @error: a #GError, or %NULL
return g_vfs_parse_name (g_vfs_get_default (), parse_name);
}
+/**
+ * g_file_new_build_filename:
+ * @first_element: (type filename): the first element in the path
+ * @...: remaining elements in path, terminated by %NULL
+ *
+ * Constructs a #GFile from a series of elements using the correct
+ * separator for filenames.
+ *
+ * Using this function is equivalent to calling g_build_filename(),
+ * followed by g_file_new_for_path() on the result.
+ *
+ * Returns: (transfer full): a new #GFile
+ *
+ * Since: 2.56
+ */
+GFile *
+g_file_new_build_filename (const gchar *first_element,
+ ...)
+{
+ gchar *str;
+ GFile *file;
+ va_list args;
+
+ g_return_val_if_fail (first_element != NULL, NULL);
+
+ va_start (args, first_element);
+ str = g_build_filename_valist (first_element, &args);
+ va_end (args);
+
+ file = g_file_new_for_path (str);
+ g_free (str);
+
+ return file;
+}
+
static gboolean
is_valid_scheme_character (char c)
{
/**
* g_file_new_for_commandline_arg:
- * @arg: a command line string
+ * @arg: (type filename): a command line string
*
* Creates a #GFile with the given argument from the command line.
* The value of @arg can be either a URI, an absolute path or a
* This operation never fails, but the returned object might not
* support any I/O operation if @arg points to a malformed path.
*
+ * Note that on Windows, this function expects its argument to be in
+ * UTF-8 -- not the system code page. This means that you
+ * should not use this function with string from argv as it is passed
+ * to main(). g_win32_get_command_line() will return a UTF-8 version of
+ * the commandline. #GApplication also uses UTF-8 but
+ * g_application_command_line_create_file_for_arg() may be more useful
+ * for you there. It is also always possible to use this function with
+ * #GOptionContext arguments of type %G_OPTION_ARG_FILENAME.
+ *
* Returns: (transfer full): a new #GFile.
* Free the returned object with g_object_unref().
*/
/**
* g_file_new_for_commandline_arg_and_cwd:
- * @arg: a command line string
- * @cwd: the current working directory of the commandline
+ * @arg: (type filename): a command line string
+ * @cwd: (type filename): the current working directory of the commandline
*
* Creates a #GFile with the given argument from the command line.
*
* g_file_mount_enclosing_volume:
* @location: input #GFile
* @flags: flags affecting the operation
- * @mount_operation: (allow-none): a #GMountOperation
+ * @mount_operation: (nullable): a #GMountOperation
* or %NULL to avoid user interaction
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
- * @callback: (allow-none): a #GAsyncReadyCallback to call
+ * @callback: (nullable): a #GAsyncReadyCallback to call
* when the request is satisfied, or %NULL
* @user_data: the data to pass to callback function
*
g_task_report_new_error (location, callback, user_data,
g_file_mount_enclosing_volume,
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("volume doesn't implement mount"));
+ _("volume doesn’t implement mount"));
return;
}
if (appinfo != NULL)
return appinfo;
}
+ else
+ g_free (uri_scheme);
info = g_file_query_info (file,
- G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+ G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE,
0,
cancellable,
error);
appinfo = NULL;
content_type = g_file_info_get_content_type (info);
+ if (content_type == NULL)
+ content_type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
if (content_type)
{
/* Don't use is_native(), as we want to support fuse paths if available */
return NULL;
}
+static void
+query_default_handler_query_info_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = G_FILE (object);
+ GTask *task = G_TASK (user_data);
+ GError *error = NULL;
+ GFileInfo *info;
+ const char *content_type;
+ GAppInfo *appinfo = NULL;
+
+ info = g_file_query_info_finish (file, result, &error);
+ if (info == NULL)
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+ return;
+ }
+
+ content_type = g_file_info_get_content_type (info);
+ if (content_type == NULL)
+ content_type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
+ if (content_type)
+ {
+ char *path;
+
+ /* Don't use is_native(), as we want to support fuse paths if available */
+ path = g_file_get_path (file);
+
+ /* FIXME: The following still uses blocking calls. */
+ appinfo = g_app_info_get_default_for_type (content_type,
+ path == NULL);
+ g_free (path);
+ }
+
+ g_object_unref (info);
+
+ if (appinfo != NULL)
+ g_task_return_pointer (task, g_steal_pointer (&appinfo), g_object_unref);
+ else
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("No application is registered as handling this file"));
+ g_object_unref (task);
+}
+
+/**
+ * g_file_query_default_handler_async:
+ * @file: a #GFile to open
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @callback: (nullable): a #GAsyncReadyCallback to call when the request is done
+ * @user_data: (nullable): data to pass to @callback
+ *
+ * Async version of g_file_query_default_handler().
+ *
+ * Since: 2.60
+ */
+void
+g_file_query_default_handler_async (GFile *file,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ char *uri_scheme;
+
+ task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_query_default_handler_async);
+
+ uri_scheme = g_file_get_uri_scheme (file);
+ if (uri_scheme && uri_scheme[0] != '\0')
+ {
+ GAppInfo *appinfo;
+
+ /* FIXME: The following still uses blocking calls. */
+ appinfo = g_app_info_get_default_for_uri_scheme (uri_scheme);
+ g_free (uri_scheme);
+
+ if (appinfo != NULL)
+ {
+ g_task_return_pointer (task, g_steal_pointer (&appinfo), g_object_unref);
+ g_object_unref (task);
+ return;
+ }
+ }
+ else
+ g_free (uri_scheme);
+
+ g_file_query_info_async (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+ G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE,
+ 0,
+ io_priority,
+ cancellable,
+ query_default_handler_query_info_cb,
+ g_steal_pointer (&task));
+}
+
+/**
+ * g_file_query_default_handler_finish:
+ * @file: a #GFile to open
+ * @result: a #GAsyncResult
+ * @error: (nullable): a #GError
+ *
+ * Finishes a g_file_query_default_handler_async() operation.
+ *
+ * Returns: (transfer full): a #GAppInfo if the handle was found,
+ * %NULL if there were errors.
+ * When you are done with it, release it with g_object_unref()
+ *
+ * Since: 2.60
+ */
+GAppInfo *
+g_file_query_default_handler_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (g_task_is_valid (result, file), NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
#define GET_CONTENT_BLOCK_SIZE 8192
/**
* @file: input #GFile
* @cancellable: optional #GCancellable object, %NULL to ignore
* @contents: (out) (transfer full) (element-type guint8) (array length=length): a location to place the contents of the file
- * @length: (out) (allow-none): a location to place the length of the contents of the file,
+ * @length: (out) (optional): a location to place the length of the contents of the file,
* or %NULL if the length is not needed
- * @etag_out: (out) (allow-none): a location to place the current entity tag for the file,
+ * @etag_out: (out) (optional): a location to place the current entity tag for the file,
* or %NULL if the entity tag is not needed
* @error: a #GError, or %NULL
*
* Loads the content of the file into memory. The data is always
* zero-terminated, but this is not included in the resultant @length.
- * The returned @content should be freed with g_free() when no longer
+ * The returned @contents should be freed with g_free() when no longer
* needed.
*
* If @cancellable is not %NULL, then the operation can be cancelled by
* g_file_load_partial_contents_async: (skip)
* @file: input #GFile
* @cancellable: optional #GCancellable object, %NULL to ignore
- * @read_more_callback: a #GFileReadMoreCallback to receive partial data
+ * @read_more_callback: (scope call) (closure user_data): a
+ * #GFileReadMoreCallback to receive partial data
* and to specify whether further data should be read
- * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @callback: (scope async) (closure user_data): a #GAsyncReadyCallback to call
+ * when the request is satisfied
* @user_data: the data to pass to the callback functions
*
* Reads the partial contents of a file. A #GFileReadMoreCallback should
data->content = g_byte_array_new ();
data->task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (data->task, g_file_load_partial_contents_async);
g_task_set_task_data (data->task, data, (GDestroyNotify)load_contents_data_free);
g_file_read_async (file,
* @file: input #GFile
* @res: a #GAsyncResult
* @contents: (out) (transfer full) (element-type guint8) (array length=length): a location to place the contents of the file
- * @length: (out) (allow-none): a location to place the length of the contents of the file,
+ * @length: (out) (optional): a location to place the length of the contents of the file,
* or %NULL if the length is not needed
- * @etag_out: (out) (allow-none): a location to place the current entity tag for the file,
+ * @etag_out: (out) (optional): a location to place the current entity tag for the file,
* or %NULL if the entity tag is not needed
* @error: a #GError, or %NULL
*
* Finishes an asynchronous partial load operation that was started
* with g_file_load_partial_contents_async(). The data is always
* zero-terminated, but this is not included in the resultant @length.
- * The returned @content should be freed with g_free() when no longer
+ * The returned @contents should be freed with g_free() when no longer
* needed.
*
* Returns: %TRUE if the load was successful. If %FALSE and @error is
* @file: input #GFile
* @res: a #GAsyncResult
* @contents: (out) (transfer full) (element-type guint8) (array length=length): a location to place the contents of the file
- * @length: (out) (allow-none): a location to place the length of the contents of the file,
+ * @length: (out) (optional): a location to place the length of the contents of the file,
* or %NULL if the length is not needed
- * @etag_out: (out) (allow-none): a location to place the current entity tag for the file,
+ * @etag_out: (out) (optional): a location to place the current entity tag for the file,
* or %NULL if the entity tag is not needed
* @error: a #GError, or %NULL
*
* Finishes an asynchronous load of the @file's contents.
* The contents are placed in @contents, and @length is set to the
- * size of the @contents string. The @content should be freed with
+ * size of the @contents string. The @contents should be freed with
* g_free() when no longer needed. If @etag_out is present, it will be
* set to the new entity tag for the @file.
*
* @file: input #GFile
* @contents: (element-type guint8) (array length=length): a string containing the new contents for @file
* @length: the length of @contents in bytes
- * @etag: (allow-none): the old <link linkend="gfile-etag">entity tag</link>
- * for the document, or %NULL
+ * @etag: (nullable): the old [entity-tag][gfile-etag] for the document,
+ * or %NULL
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
- * @new_etag: (allow-none) (out): a location to a new <link linkend="gfile-etag">entity tag</link>
+ * @new_etag: (out) (optional): a location to a new [entity tag][gfile-etag]
* for the document. This should be freed with g_free() when no longer
* needed, or %NULL
* @cancellable: optional #GCancellable object, %NULL to ignore
* or the error %G_IO_ERROR_WRONG_ETAG will be returned.
*
* If @make_backup is %TRUE, this function will attempt to make a backup
- * of @file.
+ * of @file. Internally, it uses g_file_replace(), so will try to replace the
+ * file contents in the safest way possible. For example, atomic renames are
+ * used when replacing local files’ contents.
*
* If @cancellable is not %NULL, then the operation can be cancelled by
* triggering the cancellable object from another thread. If the operation
/* Ignore errors here, we're only reading anyway */
g_output_stream_close_finish (stream, close_res, NULL);
- g_object_unref (stream);
if (!data->failed)
{
g_task_get_cancellable (data->task),
replace_contents_write_callback,
data);
+ g_object_unref (stream); /* ownership is transferred to the write_async() call above */
}
else
{
* @file: input #GFile
* @contents: (element-type guint8) (array length=length): string of contents to replace the file with
* @length: the length of @contents in bytes
- * @etag: (allow-none): a new <link linkend="gfile-etag">entity tag</link> for the @file, or %NULL
+ * @etag: (nullable): a new [entity tag][gfile-etag] for the @file, or %NULL
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
* @cancellable: optional #GCancellable object, %NULL to ignore
*
* If @make_backup is %TRUE, this function will attempt to
* make a backup of @file.
+ *
+ * Note that no copy of @contents will be made, so it must stay valid
+ * until @callback is called. See g_file_replace_contents_bytes_async()
+ * for a #GBytes version that will automatically hold a reference to the
+ * contents (without copying) for the duration of the call.
*/
void
g_file_replace_contents_async (GFile *file,
* g_file_replace_contents_bytes_async:
* @file: input #GFile
* @contents: a #GBytes
- * @etag: (allow-none): a new <link linkend="gfile-etag">entity tag</link> for the @file, or %NULL
+ * @etag: (nullable): a new [entity tag][gfile-etag] for the @file, or %NULL
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
* @cancellable: optional #GCancellable object, %NULL to ignore
data->content = g_bytes_ref (contents);
data->task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (data->task, g_file_replace_contents_bytes_async);
g_task_set_task_data (data->task, data, (GDestroyNotify)replace_contents_data_free);
g_file_replace_async (file,
* g_file_replace_contents_finish:
* @file: input #GFile
* @res: a #GAsyncResult
- * @new_etag: (out) (allow-none): a location of a new <link linkend="gfile-etag">entity tag</link>
+ * @new_etag: (out) (optional): a location of a new [entity tag][gfile-etag]
* for the document. This should be freed with g_free() when it is no
* longer needed, or %NULL
* @error: a #GError, or %NULL
{
MeasureTaskData *data = task_data;
GError *error = NULL;
- MeasureResult result;
+ MeasureResult result = { 0, };
if (g_file_measure_disk_usage (source_object, data->flags, cancellable,
data->progress_callback ? measure_disk_usage_progress : NULL, task,
data.progress_data = progress_data;
task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_measure_disk_usage_async);
g_task_set_task_data (task, g_memdup (&data, sizeof data), g_free);
g_task_set_priority (task, io_priority);
* g_file_measure_disk_usage:
* @file: a #GFile
* @flags: #GFileMeasureFlags
- * @cancellable: (allow-none): optional #GCancellable
- * @progress_callback: (allow-none): a #GFileMeasureProgressCallback
+ * @cancellable: (nullable): optional #GCancellable
+ * @progress_callback: (nullable): a #GFileMeasureProgressCallback
* @progress_data: user_data for @progress_callback
- * @disk_usage: (allow-none) (out): the number of bytes of disk space used
- * @num_dirs: (allow-none) (out): the number of directories encountered
- * @num_files: (allow-none) (out): the number of non-directories encountered
- * @error: (allow-none): %NULL, or a pointer to a %NULL #GError pointer
+ * @disk_usage: (out) (optional): the number of bytes of disk space used
+ * @num_dirs: (out) (optional): the number of directories encountered
+ * @num_files: (out) (optional): the number of non-directories encountered
+ * @error: (nullable): %NULL, or a pointer to a %NULL #GError pointer
*
* Recursively measures the disk usage of @file.
*
- * This is essentially an analog of the '<literal>du</literal>' command,
- * but it also reports the number of directories and non-directory files
- * encountered (including things like symbolic links).
+ * This is essentially an analog of the 'du' command, but it also
+ * reports the number of directories and non-directory files encountered
+ * (including things like symbolic links).
*
* By default, errors are only reported against the toplevel file
* itself. Errors found while recursing are silently ignored, unless
- * %G_FILE_DISK_USAGE_REPORT_ALL_ERRORS is given in @flags.
+ * %G_FILE_MEASURE_REPORT_ANY_ERROR is given in @flags.
*
* The returned size, @disk_usage, is in bytes and should be formatted
* with g_format_size() in order to get something reasonable for showing
* g_file_measure_disk_usage_async:
* @file: a #GFile
* @flags: #GFileMeasureFlags
- * @io_priority: the <link linkend="io-priority">I/O priority</link>
- * of the request
- * @cancellable: (allow-none): optional #GCancellable
- * @progress_callback: (allow-none): a #GFileMeasureProgressCallback
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable
+ * @progress_callback: (nullable): a #GFileMeasureProgressCallback
* @progress_data: user_data for @progress_callback
- * @callback: (allow-none): a #GAsyncReadyCallback to call when complete
+ * @callback: (nullable): a #GAsyncReadyCallback to call when complete
* @user_data: the data to pass to callback function
*
* Recursively measures the disk usage of @file.
g_return_if_fail (G_IS_FILE (file));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- return G_FILE_GET_IFACE (file)->measure_disk_usage_async (file, flags, io_priority, cancellable,
- progress_callback, progress_data,
- callback, user_data);
+ G_FILE_GET_IFACE (file)->measure_disk_usage_async (file, flags, io_priority, cancellable,
+ progress_callback, progress_data,
+ callback, user_data);
}
/**
* g_file_measure_disk_usage_finish:
* @file: a #GFile
* @result: the #GAsyncResult passed to your #GAsyncReadyCallback
- * @disk_usage: (allow-none) (out): the number of bytes of disk space used
- * @num_dirs: (allow-none) (out): the number of directories encountered
- * @num_files: (allow-none) (out): the number of non-directories encountered
- * @error: (allow-none): %NULL, or a pointer to a %NULL #GError pointer
+ * @disk_usage: (out) (optional): the number of bytes of disk space used
+ * @num_dirs: (out) (optional): the number of directories encountered
+ * @num_files: (out) (optional): the number of non-directories encountered
+ * @error: (nullable): %NULL, or a pointer to a %NULL #GError pointer
*
* Collects the results from an earlier call to
* g_file_measure_disk_usage_async(). See g_file_measure_disk_usage() for
* g_file_start_mountable:
* @file: input #GFile
* @flags: flags affecting the operation
- * @start_operation: (allow-none): a #GMountOperation, or %NULL to avoid user interaction
- * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore
- * @callback: (allow-none): a #GAsyncReadyCallback to call when the request is satisfied, or %NULL
+ * @start_operation: (nullable): a #GMountOperation, or %NULL to avoid user interaction
+ * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore
+ * @callback: (nullable): a #GAsyncReadyCallback to call when the request is satisfied, or %NULL
* @user_data: the data to pass to callback function
*
* Starts a file of type #G_FILE_TYPE_MOUNTABLE.
* g_file_stop_mountable:
* @file: input #GFile
* @flags: flags affecting the operation
- * @mount_operation: (allow-none): a #GMountOperation,
+ * @mount_operation: (nullable): a #GMountOperation,
* or %NULL to avoid user interaction.
- * @cancellable: (allow-none): optional #GCancellable object,
+ * @cancellable: (nullable): optional #GCancellable object,
* %NULL to ignore
- * @callback: (allow-none): a #GAsyncReadyCallback to call
+ * @callback: (nullable): a #GAsyncReadyCallback to call
* when the request is satisfied, or %NULL
* @user_data: the data to pass to callback function
*
* @result: a #GAsyncResult
* @error: a #GError, or %NULL
*
- * Finishes an stop operation, see g_file_stop_mountable() for details.
+ * Finishes a stop operation, see g_file_stop_mountable() for details.
*
* Finish an asynchronous stop operation that was started
* with g_file_stop_mountable().
* g_file_poll_mountable:
* @file: input #GFile
* @cancellable: optional #GCancellable object, %NULL to ignore
- * @callback: (allow-none): a #GAsyncReadyCallback to call
+ * @callback: (nullable): a #GAsyncReadyCallback to call
* when the request is satisfied, or %NULL
* @user_data: the data to pass to callback function
*
* g_file_supports_thread_contexts:
* @file: a #GFile
*
- * Checks if @file supports <link
- * linkend="g-main-context-push-thread-default-context">thread-default
- * contexts</link>. If this returns %FALSE, you cannot perform
- * asynchronous operations on @file in a thread that has a
- * thread-default context.
+ * Checks if @file supports
+ * [thread-default contexts][g-main-context-push-thread-default-context].
+ * If this returns %FALSE, you cannot perform asynchronous operations on
+ * @file in a thread that has a thread-default context.
*
* Returns: Whether or not @file supports thread-default contexts.
*
iface = G_FILE_GET_IFACE (file);
return iface->supports_thread_contexts;
}
+
+/**
+ * g_file_load_bytes:
+ * @file: a #GFile
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @etag_out: (out) (nullable) (optional): a location to place the current
+ * entity tag for the file, or %NULL if the entity tag is not needed
+ * @error: a location for a #GError or %NULL
+ *
+ * Loads the contents of @file and returns it as #GBytes.
+ *
+ * If @file is a resource:// based URI, the resulting bytes will reference the
+ * embedded resource instead of a copy. Otherwise, this is equivalent to calling
+ * g_file_load_contents() and g_bytes_new_take().
+ *
+ * For resources, @etag_out will be set to %NULL.
+ *
+ * The data contained in the resulting #GBytes is always zero-terminated, but
+ * this is not included in the #GBytes length. The resulting #GBytes should be
+ * freed with g_bytes_unref() when no longer in use.
+ *
+ * Returns: (transfer full): a #GBytes or %NULL and @error is set
+ *
+ * Since: 2.56
+ */
+GBytes *
+g_file_load_bytes (GFile *file,
+ GCancellable *cancellable,
+ gchar **etag_out,
+ GError **error)
+{
+ gchar *contents;
+ gsize len;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (etag_out != NULL)
+ *etag_out = NULL;
+
+ if (g_file_has_uri_scheme (file, "resource"))
+ {
+ GBytes *bytes;
+ gchar *uri, *unescaped;
+
+ uri = g_file_get_uri (file);
+ unescaped = g_uri_unescape_string (uri + strlen ("resource://"), NULL);
+ g_free (uri);
+
+ bytes = g_resources_lookup_data (unescaped, G_RESOURCE_LOOKUP_FLAGS_NONE, error);
+ g_free (unescaped);
+
+ return bytes;
+ }
+
+ /* contents is guaranteed to be \0 terminated */
+ if (g_file_load_contents (file, cancellable, &contents, &len, etag_out, error))
+ return g_bytes_new_take (g_steal_pointer (&contents), len);
+
+ return NULL;
+}
+
+static void
+g_file_load_bytes_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = G_FILE (object);
+ GTask *task = user_data;
+ GError *error = NULL;
+ gchar *etag = NULL;
+ gchar *contents = NULL;
+ gsize len = 0;
+
+ g_file_load_contents_finish (file, result, &contents, &len, &etag, &error);
+ g_task_set_task_data (task, g_steal_pointer (&etag), g_free);
+
+ if (error != NULL)
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ g_task_return_pointer (task,
+ g_bytes_new_take (g_steal_pointer (&contents), len),
+ (GDestroyNotify)g_bytes_unref);
+
+ g_object_unref (task);
+}
+
+/**
+ * g_file_load_bytes_async:
+ * @file: a #GFile
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the
+ * request is satisfied
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Asynchronously loads the contents of @file as #GBytes.
+ *
+ * If @file is a resource:// based URI, the resulting bytes will reference the
+ * embedded resource instead of a copy. Otherwise, this is equivalent to calling
+ * g_file_load_contents_async() and g_bytes_new_take().
+ *
+ * @callback should call g_file_load_bytes_finish() to get the result of this
+ * asynchronous operation.
+ *
+ * See g_file_load_bytes() for more information.
+ *
+ * Since: 2.56
+ */
+void
+g_file_load_bytes_async (GFile *file,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GBytes *bytes;
+ GTask *task;
+
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_load_bytes_async);
+
+ if (!g_file_has_uri_scheme (file, "resource"))
+ {
+ g_file_load_contents_async (file,
+ cancellable,
+ g_file_load_bytes_cb,
+ g_steal_pointer (&task));
+ return;
+ }
+
+ bytes = g_file_load_bytes (file, cancellable, NULL, &error);
+
+ if (bytes == NULL)
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ g_task_return_pointer (task,
+ g_steal_pointer (&bytes),
+ (GDestroyNotify)g_bytes_unref);
+
+ g_object_unref (task);
+}
+
+/**
+ * g_file_load_bytes_finish:
+ * @file: a #GFile
+ * @result: a #GAsyncResult provided to the callback
+ * @etag_out: (out) (nullable) (optional): a location to place the current
+ * entity tag for the file, or %NULL if the entity tag is not needed
+ * @error: a location for a #GError, or %NULL
+ *
+ * Completes an asynchronous request to g_file_load_bytes_async().
+ *
+ * For resources, @etag_out will be set to %NULL.
+ *
+ * The data contained in the resulting #GBytes is always zero-terminated, but
+ * this is not included in the #GBytes length. The resulting #GBytes should be
+ * freed with g_bytes_unref() when no longer in use.
+ *
+ * See g_file_load_bytes() for more information.
+ *
+ * Returns: (transfer full): a #GBytes or %NULL and @error is set
+ *
+ * Since: 2.56
+ */
+GBytes *
+g_file_load_bytes_finish (GFile *file,
+ GAsyncResult *result,
+ gchar **etag_out,
+ GError **error)
+{
+ GBytes *bytes;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (G_IS_TASK (result), NULL);
+ g_return_val_if_fail (g_task_is_valid (G_TASK (result), file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ bytes = g_task_propagate_pointer (G_TASK (result), error);
+
+ if (etag_out != NULL)
+ *etag_out = g_strdup (g_task_get_task_data (G_TASK (result)));
+
+ return bytes;
+}