gfileinputstream.c 14.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/* GIO - GLib Input, Output and Streaming Library
 * 
 * Copyright (C) 2006-2007 Red Hat, Inc.
 *
 * 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.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * 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.
 *
 * Author: Alexander Larsson <alexl@redhat.com>
 */

23
#include "config.h"
24 25 26 27 28

#include <glib.h>
#include <gfileinputstream.h>
#include <gseekable.h>
#include "gsimpleasyncresult.h"
29 30 31
#include "gcancellable.h"
#include "gasyncresult.h"
#include "gioerror.h"
32 33
#include "glibintl.h"

34 35
#include "gioalias.h"

36 37
/**
 * SECTION:gfileinputstream
Matthias Clasen's avatar
Matthias Clasen committed
38
 * @short_description: File input streaming operations
Matthias Clasen's avatar
Matthias Clasen committed
39
 * @include: gio/gio.h
40
 * @see_also: #GInputStream, #GDataInputStream, #GSeekable
41
 *
Matthias Clasen's avatar
Matthias Clasen committed
42 43 44
 * GFileInputStream provides input streams that take their
 * content from a file.
 *
Matthias Clasen's avatar
Matthias Clasen committed
45 46 47 48 49 50 51 52
 * GFileInputStream implements #GSeekable, which allows the input 
 * stream to jump to arbitrary positions in the file, provided the 
 * filesystem of the file allows it. In addition to the generic 
 * g_seekable_ API, GFileInputStream has its own API for seeking 
 * and positioning. To find the position of a file input stream, 
 * use g_file_input_stream_tell(). To find out if a file input 
 * stream supports seeking, use g_file_input_stream_can_seek().
 * To position a file input stream, use g_file_input_stream_seek().
53 54
 **/

55 56 57 58 59 60 61 62 63 64 65 66 67 68
static void       g_file_input_stream_seekable_iface_init    (GSeekableIface       *iface);
static goffset    g_file_input_stream_seekable_tell          (GSeekable            *seekable);
static gboolean   g_file_input_stream_seekable_can_seek      (GSeekable            *seekable);
static gboolean   g_file_input_stream_seekable_seek          (GSeekable            *seekable,
							      goffset               offset,
							      GSeekType             type,
							      GCancellable         *cancellable,
							      GError              **error);
static gboolean   g_file_input_stream_seekable_can_truncate  (GSeekable            *seekable);
static gboolean   g_file_input_stream_seekable_truncate      (GSeekable            *seekable,
							      goffset               offset,
							      GCancellable         *cancellable,
							      GError              **error);
static void       g_file_input_stream_real_query_info_async  (GFileInputStream     *stream,
69
							      const char           *attributes,
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
							      int                   io_priority,
							      GCancellable         *cancellable,
							      GAsyncReadyCallback   callback,
							      gpointer              user_data);
static GFileInfo *g_file_input_stream_real_query_info_finish (GFileInputStream     *stream,
							      GAsyncResult         *result,
							      GError              **error);


G_DEFINE_TYPE_WITH_CODE (GFileInputStream, g_file_input_stream, G_TYPE_INPUT_STREAM,
			 G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
						g_file_input_stream_seekable_iface_init))

struct _GFileInputStreamPrivate {
  GAsyncReadyCallback outstanding_callback;
};

static void
g_file_input_stream_class_init (GFileInputStreamClass *klass)
{
  g_type_class_add_private (klass, sizeof (GFileInputStreamPrivate));

  klass->query_info_async = g_file_input_stream_real_query_info_async;
  klass->query_info_finish = g_file_input_stream_real_query_info_finish;
}

static void
g_file_input_stream_seekable_iface_init (GSeekableIface *iface)
{
  iface->tell = g_file_input_stream_seekable_tell;
  iface->can_seek = g_file_input_stream_seekable_can_seek;
  iface->seek = g_file_input_stream_seekable_seek;
  iface->can_truncate = g_file_input_stream_seekable_can_truncate;
103
  iface->truncate_fn = g_file_input_stream_seekable_truncate;
104 105 106 107 108 109 110 111 112 113 114 115
}

static void
g_file_input_stream_init (GFileInputStream *stream)
{
  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
					      G_TYPE_FILE_INPUT_STREAM,
					      GFileInputStreamPrivate);
}

/**
 * g_file_input_stream_query_info:
116 117
 * @stream: a #GFileInputStream.
 * @attributes: a file attribute query string.
118 119 120
 * @cancellable: optional #GCancellable object, %NULL to ignore. 
 * @error: a #GError location to store the error occuring, or %NULL to 
 * ignore.
121
 *
122
 * Queries a file input stream the given @attributes. This function blocks 
Matthias Clasen's avatar
Matthias Clasen committed
123 124 125 126
 * while querying the stream. For the asynchronous (non-blocking) version 
 * of this function, see g_file_input_stream_query_info_async(). While the 
 * stream is blocked, the stream will set the pending flag internally, and 
 * 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 *
Matthias Clasen's avatar
Matthias Clasen committed
131
g_file_input_stream_query_info (GFileInputStream  *stream,
132
                                const char        *attributes,
Matthias Clasen's avatar
Matthias Clasen committed
133 134
                                GCancellable      *cancellable,
                                GError           **error)
135 136 137 138 139 140 141 142 143
{
  GFileInputStreamClass *class;
  GInputStream *input_stream;
  GFileInfo *info;
  
  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL);
  
  input_stream = G_INPUT_STREAM (stream);
  
144 145
  if (!g_input_stream_set_pending (input_stream, error))
    return NULL;
146 147 148 149
      
  info = NULL;
  
  if (cancellable)
150
    g_cancellable_push_current (cancellable);
151 152 153 154 155
  
  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
  if (class->query_info)
    info = class->query_info (stream, attributes, cancellable, error);
  else
156 157
    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
                         _("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 165 166 167
  
  return info;
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
168 169 170
async_ready_callback_wrapper (GObject      *source_object,
                              GAsyncResult *res,
                              gpointer      user_data)
171 172 173
{
  GFileInputStream *stream = G_FILE_INPUT_STREAM (source_object);

174
  g_input_stream_clear_pending (G_INPUT_STREAM (stream));
175 176 177 178 179 180 181
  if (stream->priv->outstanding_callback)
    (*stream->priv->outstanding_callback) (source_object, res, user_data);
  g_object_unref (stream);
}

/**
 * g_file_input_stream_query_info_async:
182 183
 * @stream: a #GFileInputStream.
 * @attributes: a file attribute query string.
Matthias Clasen's avatar
Matthias Clasen committed
184
 * @io_priority: the <link linkend="io-priority">I/O priority</link> 
185
 *     of the request.
186 187 188 189
 * @cancellable: optional #GCancellable object, %NULL to ignore. 
 * @callback: callback to call when the request is satisfied
 * @user_data: the data to pass to callback function
 * 
190 191 192 193 194 195 196
 * Queries the stream information asynchronously.
 * When the operation is finished @callback will be called. 
 * You can then call g_file_input_stream_query_info_finish() 
 * to get the result of the operation.
 *
 * For the synchronous version of this function, 
 * see g_file_input_stream_query_info(). 
197 198 199 200
 * 
 * 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 set
201 202 203
 *  
 **/
void
Matthias Clasen's avatar
Matthias Clasen committed
204
g_file_input_stream_query_info_async (GFileInputStream    *stream,
205
                                      const char          *attributes,
Matthias Clasen's avatar
Matthias Clasen committed
206 207 208 209
                                      int                  io_priority,
                                      GCancellable        *cancellable,
                                      GAsyncReadyCallback  callback,
                                      gpointer             user_data)
210 211 212
{
  GFileInputStreamClass *klass;
  GInputStream *input_stream;
213
  GError *error = NULL;
214 215 216 217 218

  g_return_if_fail (G_IS_FILE_INPUT_STREAM (stream));

  input_stream = G_INPUT_STREAM (stream);
  
219
  if (!g_input_stream_set_pending (input_stream, &error))
220
    {
221 222 223 224 225
      g_simple_async_report_gerror_in_idle (G_OBJECT (stream),
					    callback,
					    user_data,
					    error);
      g_error_free (error);
226 227 228 229 230 231 232 233 234 235 236 237 238
      return;
    }

  klass = G_FILE_INPUT_STREAM_GET_CLASS (stream);

  stream->priv->outstanding_callback = callback;
  g_object_ref (stream);
  klass->query_info_async (stream, attributes, io_priority, cancellable,
			      async_ready_callback_wrapper, user_data);
}

/**
 * g_file_input_stream_query_info_finish:
239 240
 * @stream: a #GFileInputStream.
 * @result: a #GAsyncResult.
Matthias Clasen's avatar
Matthias Clasen committed
241 242
 * @error: a #GError location to store the error occuring, 
 *     or %NULL to ignore.
243 244 245
 * 
 * Finishes an asynchronous info query operation.
 * 
246 247 248
 * Returns: #GFileInfo. 
 **/
GFileInfo *
Matthias Clasen's avatar
Matthias Clasen committed
249 250 251
g_file_input_stream_query_info_finish (GFileInputStream  *stream,
                                       GAsyncResult      *result,
                                       GError           **error)
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
{
  GSimpleAsyncResult *simple;
  GFileInputStreamClass *class;

  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL);
  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);

  if (G_IS_SIMPLE_ASYNC_RESULT (result))
    {
      simple = G_SIMPLE_ASYNC_RESULT (result);
      if (g_simple_async_result_propagate_error (simple, error))
	return NULL;
    }

  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
  return class->query_info_finish (stream, result, error);
}

270
static goffset
Matthias Clasen's avatar
Matthias Clasen committed
271
g_file_input_stream_tell (GFileInputStream *stream)
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
{
  GFileInputStreamClass *class;
  goffset offset;

  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), 0);

  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);

  offset = 0;
  if (class->tell)
    offset = class->tell (stream);

  return offset;
}

static goffset
g_file_input_stream_seekable_tell (GSeekable *seekable)
{
  return g_file_input_stream_tell (G_FILE_INPUT_STREAM (seekable));
}

293
static gboolean
Matthias Clasen's avatar
Matthias Clasen committed
294
g_file_input_stream_can_seek (GFileInputStream *stream)
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
{
  GFileInputStreamClass *class;
  gboolean can_seek;

  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE);

  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);

  can_seek = FALSE;
  if (class->seek)
    {
      can_seek = TRUE;
      if (class->can_seek)
	can_seek = class->can_seek (stream);
    }
  
  return can_seek;
}

static gboolean
g_file_input_stream_seekable_can_seek (GSeekable *seekable)
{
  return g_file_input_stream_can_seek (G_FILE_INPUT_STREAM (seekable));
}

320
static gboolean
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
g_file_input_stream_seek (GFileInputStream  *stream,
			  goffset            offset,
			  GSeekType          type,
			  GCancellable      *cancellable,
			  GError           **error)
{
  GFileInputStreamClass *class;
  GInputStream *input_stream;
  gboolean res;

  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE);

  input_stream = G_INPUT_STREAM (stream);
  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);

  if (!class->seek)
    {
338 339
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
                           _("Seek not supported on stream"));
340 341 342
      return FALSE;
    }

343 344
  if (!g_input_stream_set_pending (input_stream, error))
    return FALSE;
345 346
  
  if (cancellable)
347
    g_cancellable_push_current (cancellable);
348 349 350 351
  
  res = class->seek (stream, offset, type, cancellable, error);
  
  if (cancellable)
352
    g_cancellable_pop_current (cancellable);
353

354
  g_input_stream_clear_pending (input_stream);
355 356 357 358 359
  
  return res;
}

static gboolean
Matthias Clasen's avatar
Matthias Clasen committed
360 361 362
g_file_input_stream_seekable_seek (GSeekable     *seekable,
				   goffset        offset,
				   GSeekType      type,
363
				   GCancellable  *cancellable,
Matthias Clasen's avatar
Matthias Clasen committed
364
				   GError       **error)
365 366 367 368 369 370
{
  return g_file_input_stream_seek (G_FILE_INPUT_STREAM (seekable),
				   offset, type, cancellable, error);
}

static gboolean
Matthias Clasen's avatar
Matthias Clasen committed
371
g_file_input_stream_seekable_can_truncate (GSeekable *seekable)
372 373 374 375 376
{
  return FALSE;
}

static gboolean
Matthias Clasen's avatar
Matthias Clasen committed
377 378
g_file_input_stream_seekable_truncate (GSeekable     *seekable,
				       goffset        offset,
379
				       GCancellable  *cancellable,
Matthias Clasen's avatar
Matthias Clasen committed
380
				       GError       **error)
381
{
382 383
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
                       _("Truncate not allowed on input stream"));
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
  return FALSE;
}

/********************************************
 *   Default implementation of async ops    *
 ********************************************/

typedef struct {
  char *attributes;
  GFileInfo *info;
} QueryInfoAsyncData;

static void
query_info_data_free (QueryInfoAsyncData *data)
{
  if (data->info)
    g_object_unref (data->info);
  g_free (data->attributes);
  g_free (data);
}

static void
query_info_async_thread (GSimpleAsyncResult *res,
Matthias Clasen's avatar
Matthias Clasen committed
407 408
		         GObject            *object,
		         GCancellable       *cancellable)
409 410 411 412 413 414 415 416 417 418 419 420 421 422
{
  GFileInputStreamClass *class;
  GError *error = NULL;
  QueryInfoAsyncData *data;
  GFileInfo *info;
  
  data = g_simple_async_result_get_op_res_gpointer (res);

  info = NULL;
  
  class = G_FILE_INPUT_STREAM_GET_CLASS (object);
  if (class->query_info)
    info = class->query_info (G_FILE_INPUT_STREAM (object), data->attributes, cancellable, &error);
  else
423 424
    g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
                         _("Stream doesn't support query_info"));
425 426 427 428 429 430 431 432 433 434 435

  if (info == NULL)
    {
      g_simple_async_result_set_from_error (res, error);
      g_error_free (error);
    }
  else
    data->info = info;
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
436
g_file_input_stream_real_query_info_async (GFileInputStream    *stream,
437
                                           const char          *attributes,
Matthias Clasen's avatar
Matthias Clasen committed
438 439 440 441
                                           int                  io_priority,
                                           GCancellable        *cancellable,
                                           GAsyncReadyCallback  callback,
                                           gpointer             user_data)
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
{
  GSimpleAsyncResult *res;
  QueryInfoAsyncData *data;

  data = g_new0 (QueryInfoAsyncData, 1);
  data->attributes = g_strdup (attributes);
  
  res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, g_file_input_stream_real_query_info_async);
  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)query_info_data_free);
  
  g_simple_async_result_run_in_thread (res, query_info_async_thread, io_priority, cancellable);
  g_object_unref (res);
}

static GFileInfo *
Matthias Clasen's avatar
Matthias Clasen committed
457 458 459
g_file_input_stream_real_query_info_finish (GFileInputStream  *stream,
                                            GAsyncResult      *res,
                                            GError           **error)
460 461 462 463
{
  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
  QueryInfoAsyncData *data;

464
  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_input_stream_real_query_info_async);
465 466 467 468 469 470 471

  data = g_simple_async_result_get_op_res_gpointer (simple);
  if (data->info)
    return g_object_ref (data->info);
  
  return NULL;
}
472 473 474 475

#define __G_FILE_INPUT_STREAM_C__
#include "gioaliasdef.c"