Fix up last commit.
[platform/upstream/glib.git] / gio / gfile.c
index ae00c5a..9e65a4b 100644 (file)
@@ -1,3 +1,5 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
 /* GIO - GLib Input, Output and Streaming Library
  * 
  * Copyright (C) 2006-2007 Red Hat, Inc.
 /**
  * SECTION:gfile
  * @short_description: File and Directory Handling
- * @include: gio.h
+ * @include: gio/gio.h
  * @see_also: #GFileInfo, #GFileEnumerator
  * 
  * #GFile is a high level abstraction for manipulating files on a 
  * virtual file system. #GFile<!-- -->s are lightweight, immutable 
  * objects that do no I/O upon creation. It is necessary to understand that
- * #GFile objects do not represent files, merely a handle to a file. All
- * file I/O is implemented as streaming operations (see #GInputStream and 
+ * #GFile objects do not represent files, merely an identifier for a file. All
+ * file content I/O is implemented as streaming operations (see #GInputStream and 
  * #GOutputStream).
- * 
+ *
  * To construct a #GFile, you can use: 
  * 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.
- * 
- * You can move through the file system with #GFile handles with
- * g_file_get_parent() to get a handle to the parent directory.
- * g_file_get_child() to get a handle to a child within a directory.
- * g_file_resolve_relative_path() to resolve a relative path between
- * two #GFile<!-- -->s.
+ * g_file_parse_name() from a utf8 string gotten from g_file_get_parse_name().
+ * 
+ * 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 #GFile<!-- -->s
+ * are extensible it could also be something else that corresponds to a pathname
+ * in a userspace implementation of a filesystem.
+ *
+ * #GFile<!-- -->s make up hierarchies of directories and files that correspond to the
+ * files on a filesystem. You can move through the file system with #GFile using
+ * g_file_get_parent() to get an identifier for the parent directory, g_file_get_child()
+ * to get a child within a directory, g_file_resolve_relative_path() to resolve a relative
+ * path between two #GFile<!-- -->s. There can be multiple hierarchies, so you may not
+ * end up at the same root if you repeatedly call g_file_get_parent() on two different
+ * files.
+ *
+ * All #GFile<!-- -->s have a basename (get with g_file_get_basename()). These names
+ * are byte strings that are used to identify the file on the filesystem (relative to
+ * its parent directory) and there is no guarantees that they have any particular charset
+ * encoding or even make any sense at all. If you want to use filenames in a user
+ * interface you should use the display name that you can get by requesting the
+ * %G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME attribute with g_file_query_info().
+ * This is guaranteed to be in utf8 and can be used in a user interface. But always
+ * store the real basename or the #GFile to use to actually access the file, because
+ * there is no way to go from a display name to the actual name.
+ *
+ * Using #GFile as an identifier has the same weaknesses as using a path in that
+ * there may be multiple aliases for the same file. For instance, hard or
+ * soft links may cause two different #GFile<!-- -->s to refer to the same file.
+ * Other possible causes for aliases are: case insensitive filesystems, short
+ * and long names on Fat/NTFS, or bind mounts in linux. If you want to check if
+ * two #GFile<!-- -->s point to the same file you can query for the
+ * %G_FILE_ATTRIBUTE_ID_FILE attribute. Note that #GFile does some trivial
+ * canonicalization of pathnames passed in, so that trivial differences in the
+ * path string used at creation (dupplicated slashes, slash at end of path, "."
+ * or ".." path segments, etc) does not create different #GFile<!-- -->s.
  * 
  * Many #GFile operations have both synchronous and asynchronous versions 
  * to suit your application. Asynchronous versions of synchronous functions 
@@ -100,6 +131,15 @@ static void               g_file_real_query_info_async            (GFile
 static GFileInfo *        g_file_real_query_info_finish           (GFile                  *file,
                                                                   GAsyncResult           *res,
                                                                   GError                **error);
+static void               g_file_real_query_filesystem_info_async (GFile                  *file,
+                                                                  const char             *attributes,
+                                                                  int                     io_priority,
+                                                                  GCancellable           *cancellable,
+                                                                  GAsyncReadyCallback     callback,
+                                                                  gpointer                user_data);
+static GFileInfo *        g_file_real_query_filesystem_info_finish (GFile                  *file,
+                                                                  GAsyncResult           *res,
+                                                                  GError                **error);
 static void               g_file_real_enumerate_children_async    (GFile                  *file,
                                                                   const char             *attributes,
                                                                   GFileQueryInfoFlags     flags,
@@ -235,6 +275,8 @@ g_file_class_init (gpointer g_class,
   iface->set_display_name_finish = g_file_real_set_display_name_finish;
   iface->query_info_async = g_file_real_query_info_async;
   iface->query_info_finish = g_file_real_query_info_finish;
+  iface->query_filesystem_info_async = g_file_real_query_filesystem_info_async;
+  iface->query_filesystem_info_finish = g_file_real_query_filesystem_info_finish;
   iface->set_attributes_async = g_file_real_set_attributes_async;
   iface->set_attributes_finish = g_file_real_set_attributes_finish;
   iface->read_async = g_file_real_read_async;
@@ -355,9 +397,15 @@ g_file_get_uri_scheme (GFile *file)
  * Gets the base name (the last component of the path) for a given #GFile.
  *
  * If called for the top level of a system (such as the filesystem root
- * or a uri like sftp://host/ it will return a single directory separator
+ * or a uri like sftp://host/) it will return a single directory separator
  * (and on Windows, possibly a drive letter).
  *
+ * The base name is a byte string (*not* UTF-8). It has no defined encoding
+ * or rules other than it may not contain zero bytes.  If you want to use
+ * filenames in a user interface you should use the display name that you
+ * can get by requesting the %G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
+ * attribute with g_file_query_info().
+ * 
  * This call does no blocking i/o.
  * 
  * Returns: string containing the #GFile's base name, or %NULL 
@@ -433,7 +481,8 @@ g_file_get_uri (GFile *file)
  * g_file_parse_name().
  *
  * This is generally used to show the #GFile as a nice
- * string in a user interface, like in a location entry.
+ * full-pathname kind of string in a user interface,
+ * like in a location entry.
  *
  * For local files with names that can safely be converted
  * to UTF8 the pathname is used, otherwise the IRI is used
@@ -510,7 +559,10 @@ g_file_hash (gconstpointer file)
  * @file1: the first #GFile.
  * @file2: the second #GFile.
  *
- * Checks equality of two given #GFile<!-- -->s
+ * Checks equality of two given #GFile<!-- -->s. Note that two
+ * #GFile<!-- -->s that differ can still refer to the same
+ * file on the filesystem due to various forms of filename
+ * aliasing.
  *
  * This call does no blocking i/o.
  * 
@@ -563,9 +615,9 @@ g_file_get_parent (GFile *file)
 /**
  * g_file_get_child:
  * @file: input #GFile.
- * @name: string containing the child's name.
+ * @name: string containing the child's basename.
  *
- * Gets a specific child of @file with name equal to @name.
+ * Gets a child of @file with basename equal to @name.
  *
  * Note that the file with that specific name might not exist, but
  * you can still have a #GFile that points to it. You can use this
@@ -618,31 +670,36 @@ g_file_get_child_for_display_name (GFile      *file,
 }
 
 /**
- * g_file_contains_file:
- * @parent: input #GFile.
- * @descendant: input #GFile.
- * 
- * Checks whether @parent (recursively) contains the specified @descendant.
+ * g_file_has_prefix:
+ * @file: input #GFile.
+ * @prefix: input #GFile.
  * 
- * This call does no blocking i/o.
+ * Checks whether @file has the prefix specified by @prefix. In other word, if the
+ * names of inital elements of @file<!-- -->s pathname match @prefix.
  * 
- * Returns:  %TRUE if the @descendant's parent, grandparent, etc is @parent. %FALSE otherwise.
+ * 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.
+ *
+ * Returns:  %TRUE if the @files's parent, grandparent, etc is @prefix. %FALSE otherwise.
  **/
 gboolean
-g_file_contains_file (GFile *parent,
-                     GFile *descendant)
+g_file_has_prefix (GFile *file,
+                  GFile *prefix)
 {
   GFileIface *iface;
   
-  g_return_val_if_fail (G_IS_FILE (parent), FALSE);
-  g_return_val_if_fail (G_IS_FILE (descendant), FALSE);
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_FILE (prefix), FALSE);
 
-  if (G_TYPE_FROM_INSTANCE (parent) != G_TYPE_FROM_INSTANCE (descendant))
+  if (G_TYPE_FROM_INSTANCE (file) != G_TYPE_FROM_INSTANCE (prefix))
     return FALSE;
   
-  iface = G_FILE_GET_IFACE (parent);
+  iface = G_FILE_GET_IFACE (file);
 
-  return (* iface->contains_file) (parent, descendant);
+  /* The vtable function differs in arg order since we're
+     using the old contains_file call */
+  return (* iface->prefix_matches) (prefix, file);
 }
 
 /**
@@ -655,7 +712,7 @@ g_file_contains_file (GFile *parent,
  * This call does no blocking i/o.
  * 
  * Returns: string with the relative path from @descendant 
- *     to @parent, or %NULL if @descendant is not a descendant of @parent. The returned string should be freed with 
+ *     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 *
@@ -844,9 +901,9 @@ g_file_enumerate_children_finish (GFile         *file,
  * 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 existance
+ * Note that in many cases it is racy to first check for file existence
  * and then execute something based on the outcome of that, because the
- * file might have been created or removed inbetween the operations. 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
  * operation and handle the errors as they come.
  *
@@ -858,7 +915,7 @@ g_file_enumerate_children_finish (GFile         *file,
  * the file with g_file_create() which will either atomically create the file
  * or fail with a G_IO_ERROR_EXISTS error.
  *
- * However, in many cases an existance check is useful in a user
+ * However, in many cases an existence check is useful in a user
  * interface, for instance to make a menu item sensitive/insensitive, so that
  * you don't have to fool users that something is possible and then just show
  * and error dialog. If you do this, you should make sure to also handle the
@@ -871,7 +928,9 @@ g_file_query_exists (GFile *file,
                     GCancellable *cancellable)
 {
   GFileInfo *info;
-
+  
+  g_return_val_if_fail (G_IS_FILE(file), FALSE);
+  
   info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE,
                            G_FILE_QUERY_INFO_NONE,
                            cancellable, NULL);
@@ -1081,6 +1140,81 @@ g_file_query_filesystem_info (GFile         *file,
 }
 
 /**
+ * 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: optional #GCancellable object, %NULL to ignore. 
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ * 
+ * Asynchronously gets the requested information about the filesystem
+ * that the specified @file is on. The result is a #GFileInfo object
+ * that contains key-value attributes (such as type or size for the
+ * file).
+ * 
+ * For more details, see g_file_query_filesystem_info() which is the
+ * synchronous version of this call.
+ *
+ * When the operation is finished, @callback will be called. You can
+ * then call g_file_query_info_finish() to get the result of the
+ * operation.
+ **/
+void
+g_file_query_filesystem_info_async (GFile               *file,
+                                    const char          *attributes,
+                                    int                  io_priority,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
+{
+  GFileIface *iface;
+  
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->query_filesystem_info_async) (file,
+                                          attributes,
+                                          io_priority,
+                                          cancellable,
+                                          callback,
+                                          user_data);
+}
+
+/**
+ * g_file_query_filesystem_info_finish:
+ * @file: input #GFile.
+ * @res: a #GAsyncResult. 
+ * @error: a #GError. 
+ * 
+ * Finishes an asynchronous filesystem info query.  See
+ * g_file_query_filesystem_info_async().
+ * 
+ * Returns: #GFileInfo for given @file or %NULL on error.
+ **/
+GFileInfo *
+g_file_query_filesystem_info_finish (GFile         *file,
+                                     GAsyncResult  *res,
+                                     GError       **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->query_filesystem_info_finish) (file, res, error);
+}
+
+/**
  * g_file_find_enclosing_mount:
  * @file: input #GFile.
  * @cancellable: optional #GCancellable object, %NULL to ignore. 
@@ -1106,19 +1240,21 @@ g_file_find_enclosing_mount (GFile         *file,
   GFileIface *iface;
 
   g_return_val_if_fail (G_IS_FILE (file), NULL);
-  
+
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return NULL;
 
   iface = G_FILE_GET_IFACE (file);
   if (iface->find_enclosing_mount == NULL)
     {
-      g_set_error (error, G_IO_ERROR,
-                  G_IO_ERROR_NOT_FOUND,
+
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                       /* Translators: This is an error message when trying to find the
+                        * enclosing (user visible) mount of a file, but none exists. */
                   _("Containing mount does not exist"));
       return NULL;
     }
-  
+
   return (* iface->find_enclosing_mount) (file, cancellable, error);
 }
 /**
@@ -1981,6 +2117,7 @@ g_file_copy_attributes (GFile           *source,
 static gboolean
 copy_stream_with_progress (GInputStream           *in,
                           GOutputStream          *out,
+                           GFile                  *source,
                           GCancellable           *cancellable,
                           GFileProgressCallback   progress_callback,
                           gpointer                progress_callback_data,
@@ -1988,20 +2125,38 @@ copy_stream_with_progress (GInputStream           *in,
 {
   gssize n_read, n_written;
   goffset current_size;
-  char buffer[8192], *p;
+  char buffer[1024*64], *p;
   gboolean res;
   goffset total_size;
   GFileInfo *info;
 
-  total_size = 0;
+  total_size = -1;
   info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (in),
                                         G_FILE_ATTRIBUTE_STANDARD_SIZE,
                                         cancellable, NULL);
   if (info)
     {
-      total_size = g_file_info_get_size (info);
+      if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
+        total_size = g_file_info_get_size (info);
       g_object_unref (info);
     }
+
+  if (total_size == -1)
+    {
+      info = g_file_query_info (source, 
+                                G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                                G_FILE_QUERY_INFO_NONE,
+                                cancellable, NULL);
+      if (info)
+        {
+          if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
+            total_size = g_file_info_get_size (info);
+          g_object_unref (info);
+        }
+    }
+
+  if (total_size == -1)
+    total_size = 0;
   
   current_size = 0;
   res = TRUE;
@@ -2035,7 +2190,7 @@ copy_stream_with_progress (GInputStream           *in,
 
       if (!res)
         break;
-      
+
       if (progress_callback)
        progress_callback (current_size, total_size, progress_callback_data);
     }
@@ -2046,7 +2201,6 @@ copy_stream_with_progress (GInputStream           *in,
   /* Make sure we send full copied size */
   if (progress_callback)
     progress_callback (current_size, total_size, progress_callback_data);
-
   
   /* Don't care about errors in source here */
   g_input_stream_close (in, cancellable, NULL);
@@ -2124,7 +2278,7 @@ file_copy_fallback (GFile                  *source,
       return FALSE;
     }
 
-  if (!copy_stream_with_progress (in, out, cancellable,
+  if (!copy_stream_with_progress (in, out, source, cancellable,
                                  progress_callback, progress_callback_data,
                                  error))
     return FALSE;
@@ -2563,7 +2717,8 @@ g_file_make_symbolic_link (GFile         *file,
  * @cancellable: optional #GCancellable object, %NULL to ignore.
  * @error: a #GError, or %NULL 
  * 
- * Deletes a file.
+ * Deletes a file. If the @file is a directory, it will only be deleted if it 
+ * is empty.
  * 
  * If @cancellable is not %NULL, then the operation can be cancelled by
  * triggering the cancellable object from another thread. If the operation
@@ -3695,6 +3850,80 @@ g_file_real_query_info_finish (GFile         *file,
 
 typedef struct {
   char *attributes;
+  GFileInfo *info;
+} QueryFilesystemInfoAsyncData;
+
+static void
+query_filesystem_info_data_free (QueryFilesystemInfoAsyncData *data)
+{
+  if (data->info)
+    g_object_unref (data->info);
+  g_free (data->attributes);
+  g_free (data);
+}
+
+static void
+query_filesystem_info_async_thread (GSimpleAsyncResult *res,
+                                    GObject            *object,
+                                    GCancellable       *cancellable)
+{
+  GError *error = NULL;
+  QueryFilesystemInfoAsyncData *data;
+  GFileInfo *info;
+  
+  data = g_simple_async_result_get_op_res_gpointer (res);
+  
+  info = g_file_query_filesystem_info (G_FILE (object), data->attributes, cancellable, &error);
+
+  if (info == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    data->info = info;
+}
+
+static void
+g_file_real_query_filesystem_info_async (GFile               *file,
+                                         const char          *attributes,
+                                         int                  io_priority,
+                                         GCancellable        *cancellable,
+                                         GAsyncReadyCallback  callback,
+                                         gpointer             user_data)
+{
+  GSimpleAsyncResult *res;
+  QueryFilesystemInfoAsyncData *data;
+
+  data = g_new0 (QueryFilesystemInfoAsyncData, 1);
+  data->attributes = g_strdup (attributes);
+  
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_query_filesystem_info_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)query_filesystem_info_data_free);
+  
+  g_simple_async_result_run_in_thread (res, query_filesystem_info_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileInfo *
+g_file_real_query_filesystem_info_finish (GFile         *file,
+                                          GAsyncResult  *res,
+                                          GError       **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  QueryFilesystemInfoAsyncData *data;
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_real_query_filesystem_info_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  if (data->info)
+    return g_object_ref (data->info);
+  
+  return NULL;
+}
+
+typedef struct {
+  char *attributes;
   GFileQueryInfoFlags flags;
   GFileEnumerator *enumerator;
 } EnumerateChildrenAsyncData;