Home | History | Annotate | Download | only in gio
      1 /* GIO - GLib Input, Output and Streaming Library
      2  *
      3  * Copyright (C) 2006-2007 Red Hat, Inc.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Lesser General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Lesser General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Lesser General
     16  * Public License along with this library; if not, write to the
     17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
     18  * Boston, MA 02111-1307, USA.
     19  *
     20  * Author: Alexander Larsson <alexl (at) redhat.com>
     21  */
     22 
     23 #include "config.h"
     24 
     25 #include <glib.h>
     26 #include <gfileinputstream.h>
     27 #include <gseekable.h>
     28 #include "gsimpleasyncresult.h"
     29 #include "gcancellable.h"
     30 #include "gasyncresult.h"
     31 #include "gioerror.h"
     32 #include "glibintl.h"
     33 
     34 #include "gioalias.h"
     35 
     36 /**
     37  * SECTION:gfileinputstream
     38  * @short_description: File input streaming operations
     39  * @include: gio/gio.h
     40  * @see_also: #GInputStream, #GDataInputStream, #GSeekable
     41  *
     42  * GFileInputStream provides input streams that take their
     43  * content from a file.
     44  *
     45  * GFileInputStream implements #GSeekable, which allows the input
     46  * stream to jump to arbitrary positions in the file, provided the
     47  * filesystem of the file allows it. In addition to the generic
     48  * g_seekable_ API, GFileInputStream has its own API for seeking
     49  * and positioning. To find the position of a file input stream,
     50  * use g_file_input_stream_tell(). To find out if a file input
     51  * stream supports seeking, use g_file_input_stream_can_seek().
     52  * To position a file input stream, use g_file_input_stream_seek().
     53  **/
     54 
     55 static void       g_file_input_stream_seekable_iface_init    (GSeekableIface       *iface);
     56 static goffset    g_file_input_stream_seekable_tell          (GSeekable            *seekable);
     57 static gboolean   g_file_input_stream_seekable_can_seek      (GSeekable            *seekable);
     58 static gboolean   g_file_input_stream_seekable_seek          (GSeekable            *seekable,
     59 							      goffset               offset,
     60 							      GSeekType             type,
     61 							      GCancellable         *cancellable,
     62 							      GError              **error);
     63 static gboolean   g_file_input_stream_seekable_can_truncate  (GSeekable            *seekable);
     64 static gboolean   g_file_input_stream_seekable_truncate      (GSeekable            *seekable,
     65 							      goffset               offset,
     66 							      GCancellable         *cancellable,
     67 							      GError              **error);
     68 static void       g_file_input_stream_real_query_info_async  (GFileInputStream     *stream,
     69 							      const char           *attributes,
     70 							      int                   io_priority,
     71 							      GCancellable         *cancellable,
     72 							      GAsyncReadyCallback   callback,
     73 							      gpointer              user_data);
     74 static GFileInfo *g_file_input_stream_real_query_info_finish (GFileInputStream     *stream,
     75 							      GAsyncResult         *result,
     76 							      GError              **error);
     77 
     78 
     79 G_DEFINE_TYPE_WITH_CODE (GFileInputStream, g_file_input_stream, G_TYPE_INPUT_STREAM,
     80 			 G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
     81 						g_file_input_stream_seekable_iface_init))
     82 
     83 struct _GFileInputStreamPrivate {
     84   GAsyncReadyCallback outstanding_callback;
     85 };
     86 
     87 static void
     88 g_file_input_stream_class_init (GFileInputStreamClass *klass)
     89 {
     90   g_type_class_add_private (klass, sizeof (GFileInputStreamPrivate));
     91 
     92   klass->query_info_async = g_file_input_stream_real_query_info_async;
     93   klass->query_info_finish = g_file_input_stream_real_query_info_finish;
     94 }
     95 
     96 static void
     97 g_file_input_stream_seekable_iface_init (GSeekableIface *iface)
     98 {
     99   iface->tell = g_file_input_stream_seekable_tell;
    100   iface->can_seek = g_file_input_stream_seekable_can_seek;
    101   iface->seek = g_file_input_stream_seekable_seek;
    102   iface->can_truncate = g_file_input_stream_seekable_can_truncate;
    103   iface->truncate_fn = g_file_input_stream_seekable_truncate;
    104 }
    105 
    106 static void
    107 g_file_input_stream_init (GFileInputStream *stream)
    108 {
    109   stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
    110 					      G_TYPE_FILE_INPUT_STREAM,
    111 					      GFileInputStreamPrivate);
    112 }
    113 
    114 /**
    115  * g_file_input_stream_query_info:
    116  * @stream: a #GFileInputStream.
    117  * @attributes: a file attribute query string.
    118  * @cancellable: optional #GCancellable object, %NULL to ignore.
    119  * @error: a #GError location to store the error occuring, or %NULL to
    120  * ignore.
    121  *
    122  * Queries a file input stream the given @attributes. This function blocks
    123  * while querying the stream. For the asynchronous (non-blocking) version
    124  * of this function, see g_file_input_stream_query_info_async(). While the
    125  * stream is blocked, the stream will set the pending flag internally, and
    126  * any other operations on the stream will fail with %G_IO_ERROR_PENDING.
    127  *
    128  * Returns: a #GFileInfo, or %NULL on error.
    129  **/
    130 GFileInfo *
    131 g_file_input_stream_query_info (GFileInputStream  *stream,
    132                                 const char        *attributes,
    133                                 GCancellable      *cancellable,
    134                                 GError           **error)
    135 {
    136   GFileInputStreamClass *class;
    137   GInputStream *input_stream;
    138   GFileInfo *info;
    139 
    140   g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL);
    141 
    142   input_stream = G_INPUT_STREAM (stream);
    143 
    144   if (!g_input_stream_set_pending (input_stream, error))
    145     return NULL;
    146 
    147   info = NULL;
    148 
    149   if (cancellable)
    150     g_cancellable_push_current (cancellable);
    151 
    152   class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
    153   if (class->query_info)
    154     info = class->query_info (stream, attributes, cancellable, error);
    155   else
    156     g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
    157                          _("Stream doesn't support query_info"));
    158 
    159   if (cancellable)
    160     g_cancellable_pop_current (cancellable);
    161 
    162   g_input_stream_clear_pending (input_stream);
    163 
    164   return info;
    165 }
    166 
    167 static void
    168 async_ready_callback_wrapper (GObject      *source_object,
    169                               GAsyncResult *res,
    170                               gpointer      user_data)
    171 {
    172   GFileInputStream *stream = G_FILE_INPUT_STREAM (source_object);
    173 
    174   g_input_stream_clear_pending (G_INPUT_STREAM (stream));
    175   if (stream->priv->outstanding_callback)
    176     (*stream->priv->outstanding_callback) (source_object, res, user_data);
    177   g_object_unref (stream);
    178 }
    179 
    180 /**
    181  * g_file_input_stream_query_info_async:
    182  * @stream: a #GFileInputStream.
    183  * @attributes: a file attribute query string.
    184  * @io_priority: the <link linkend="io-priority">I/O priority</link>
    185  *     of the request.
    186  * @cancellable: optional #GCancellable object, %NULL to ignore.
    187  * @callback: callback to call when the request is satisfied
    188  * @user_data: the data to pass to callback function
    189  *
    190  * Queries the stream information asynchronously.
    191  * When the operation is finished @callback will be called.
    192  * You can then call g_file_input_stream_query_info_finish()
    193  * to get the result of the operation.
    194  *
    195  * For the synchronous version of this function,
    196  * see g_file_input_stream_query_info().
    197  *
    198  * If @cancellable is not %NULL, then the operation can be cancelled by
    199  * triggering the cancellable object from another thread. If the operation
    200  * was cancelled, the error %G_IO_ERROR_CANCELLED will be set
    201  *
    202  **/
    203 void
    204 g_file_input_stream_query_info_async (GFileInputStream    *stream,
    205                                       const char          *attributes,
    206                                       int                  io_priority,
    207                                       GCancellable        *cancellable,
    208                                       GAsyncReadyCallback  callback,
    209                                       gpointer             user_data)
    210 {
    211   GFileInputStreamClass *klass;
    212   GInputStream *input_stream;
    213   GError *error = NULL;
    214 
    215   g_return_if_fail (G_IS_FILE_INPUT_STREAM (stream));
    216 
    217   input_stream = G_INPUT_STREAM (stream);
    218 
    219   if (!g_input_stream_set_pending (input_stream, &error))
    220     {
    221       g_simple_async_report_gerror_in_idle (G_OBJECT (stream),
    222 					    callback,
    223 					    user_data,
    224 					    error);
    225       g_error_free (error);
    226       return;
    227     }
    228 
    229   klass = G_FILE_INPUT_STREAM_GET_CLASS (stream);
    230 
    231   stream->priv->outstanding_callback = callback;
    232   g_object_ref (stream);
    233   klass->query_info_async (stream, attributes, io_priority, cancellable,
    234 			      async_ready_callback_wrapper, user_data);
    235 }
    236 
    237 /**
    238  * g_file_input_stream_query_info_finish:
    239  * @stream: a #GFileInputStream.
    240  * @result: a #GAsyncResult.
    241  * @error: a #GError location to store the error occuring,
    242  *     or %NULL to ignore.
    243  *
    244  * Finishes an asynchronous info query operation.
    245  *
    246  * Returns: #GFileInfo.
    247  **/
    248 GFileInfo *
    249 g_file_input_stream_query_info_finish (GFileInputStream  *stream,
    250                                        GAsyncResult      *result,
    251                                        GError           **error)
    252 {
    253   GSimpleAsyncResult *simple;
    254   GFileInputStreamClass *class;
    255 
    256   g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL);
    257   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
    258 
    259   if (G_IS_SIMPLE_ASYNC_RESULT (result))
    260     {
    261       simple = G_SIMPLE_ASYNC_RESULT (result);
    262       if (g_simple_async_result_propagate_error (simple, error))
    263 	return NULL;
    264     }
    265 
    266   class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
    267   return class->query_info_finish (stream, result, error);
    268 }
    269 
    270 static goffset
    271 g_file_input_stream_tell (GFileInputStream *stream)
    272 {
    273   GFileInputStreamClass *class;
    274   goffset offset;
    275 
    276   g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), 0);
    277 
    278   class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
    279 
    280   offset = 0;
    281   if (class->tell)
    282     offset = class->tell (stream);
    283 
    284   return offset;
    285 }
    286 
    287 static goffset
    288 g_file_input_stream_seekable_tell (GSeekable *seekable)
    289 {
    290   return g_file_input_stream_tell (G_FILE_INPUT_STREAM (seekable));
    291 }
    292 
    293 static gboolean
    294 g_file_input_stream_can_seek (GFileInputStream *stream)
    295 {
    296   GFileInputStreamClass *class;
    297   gboolean can_seek;
    298 
    299   g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE);
    300 
    301   class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
    302 
    303   can_seek = FALSE;
    304   if (class->seek)
    305     {
    306       can_seek = TRUE;
    307       if (class->can_seek)
    308 	can_seek = class->can_seek (stream);
    309     }
    310 
    311   return can_seek;
    312 }
    313 
    314 static gboolean
    315 g_file_input_stream_seekable_can_seek (GSeekable *seekable)
    316 {
    317   return g_file_input_stream_can_seek (G_FILE_INPUT_STREAM (seekable));
    318 }
    319 
    320 static gboolean
    321 g_file_input_stream_seek (GFileInputStream  *stream,
    322 			  goffset            offset,
    323 			  GSeekType          type,
    324 			  GCancellable      *cancellable,
    325 			  GError           **error)
    326 {
    327   GFileInputStreamClass *class;
    328   GInputStream *input_stream;
    329   gboolean res;
    330 
    331   g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE);
    332 
    333   input_stream = G_INPUT_STREAM (stream);
    334   class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
    335 
    336   if (!class->seek)
    337     {
    338       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
    339                            _("Seek not supported on stream"));
    340       return FALSE;
    341     }
    342 
    343   if (!g_input_stream_set_pending (input_stream, error))
    344     return FALSE;
    345 
    346   if (cancellable)
    347     g_cancellable_push_current (cancellable);
    348 
    349   res = class->seek (stream, offset, type, cancellable, error);
    350 
    351   if (cancellable)
    352     g_cancellable_pop_current (cancellable);
    353 
    354   g_input_stream_clear_pending (input_stream);
    355 
    356   return res;
    357 }
    358 
    359 static gboolean
    360 g_file_input_stream_seekable_seek (GSeekable     *seekable,
    361 				   goffset        offset,
    362 				   GSeekType      type,
    363 				   GCancellable  *cancellable,
    364 				   GError       **error)
    365 {
    366   return g_file_input_stream_seek (G_FILE_INPUT_STREAM (seekable),
    367 				   offset, type, cancellable, error);
    368 }
    369 
    370 static gboolean
    371 g_file_input_stream_seekable_can_truncate (GSeekable *seekable)
    372 {
    373   return FALSE;
    374 }
    375 
    376 static gboolean
    377 g_file_input_stream_seekable_truncate (GSeekable     *seekable,
    378 				       goffset        offset,
    379 				       GCancellable  *cancellable,
    380 				       GError       **error)
    381 {
    382   g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
    383                        _("Truncate not allowed on input stream"));
    384   return FALSE;
    385 }
    386 
    387 /********************************************
    388  *   Default implementation of async ops    *
    389  ********************************************/
    390 
    391 typedef struct {
    392   char *attributes;
    393   GFileInfo *info;
    394 } QueryInfoAsyncData;
    395 
    396 static void
    397 query_info_data_free (QueryInfoAsyncData *data)
    398 {
    399   if (data->info)
    400     g_object_unref (data->info);
    401   g_free (data->attributes);
    402   g_free (data);
    403 }
    404 
    405 static void
    406 query_info_async_thread (GSimpleAsyncResult *res,
    407 		         GObject            *object,
    408 		         GCancellable       *cancellable)
    409 {
    410   GFileInputStreamClass *class;
    411   GError *error = NULL;
    412   QueryInfoAsyncData *data;
    413   GFileInfo *info;
    414 
    415   data = g_simple_async_result_get_op_res_gpointer (res);
    416 
    417   info = NULL;
    418 
    419   class = G_FILE_INPUT_STREAM_GET_CLASS (object);
    420   if (class->query_info)
    421     info = class->query_info (G_FILE_INPUT_STREAM (object), data->attributes, cancellable, &error);
    422   else
    423     g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
    424                          _("Stream doesn't support query_info"));
    425 
    426   if (info == NULL)
    427     {
    428       g_simple_async_result_set_from_error (res, error);
    429       g_error_free (error);
    430     }
    431   else
    432     data->info = info;
    433 }
    434 
    435 static void
    436 g_file_input_stream_real_query_info_async (GFileInputStream    *stream,
    437                                            const char          *attributes,
    438                                            int                  io_priority,
    439                                            GCancellable        *cancellable,
    440                                            GAsyncReadyCallback  callback,
    441                                            gpointer             user_data)
    442 {
    443   GSimpleAsyncResult *res;
    444   QueryInfoAsyncData *data;
    445 
    446   data = g_new0 (QueryInfoAsyncData, 1);
    447   data->attributes = g_strdup (attributes);
    448 
    449   res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, g_file_input_stream_real_query_info_async);
    450   g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)query_info_data_free);
    451 
    452   g_simple_async_result_run_in_thread (res, query_info_async_thread, io_priority, cancellable);
    453   g_object_unref (res);
    454 }
    455 
    456 static GFileInfo *
    457 g_file_input_stream_real_query_info_finish (GFileInputStream  *stream,
    458                                             GAsyncResult      *res,
    459                                             GError           **error)
    460 {
    461   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
    462   QueryInfoAsyncData *data;
    463 
    464   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_input_stream_real_query_info_async);
    465 
    466   data = g_simple_async_result_get_op_res_gpointer (simple);
    467   if (data->info)
    468     return g_object_ref (data->info);
    469 
    470   return NULL;
    471 }
    472 
    473 #define __G_FILE_INPUT_STREAM_C__
    474 #include "gioaliasdef.c"
    475 
    476