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@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 55static void g_file_input_stream_seekable_iface_init (GSeekableIface *iface); 56static goffset g_file_input_stream_seekable_tell (GSeekable *seekable); 57static gboolean g_file_input_stream_seekable_can_seek (GSeekable *seekable); 58static gboolean g_file_input_stream_seekable_seek (GSeekable *seekable, 59 goffset offset, 60 GSeekType type, 61 GCancellable *cancellable, 62 GError **error); 63static gboolean g_file_input_stream_seekable_can_truncate (GSeekable *seekable); 64static gboolean g_file_input_stream_seekable_truncate (GSeekable *seekable, 65 goffset offset, 66 GCancellable *cancellable, 67 GError **error); 68static 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); 74static GFileInfo *g_file_input_stream_real_query_info_finish (GFileInputStream *stream, 75 GAsyncResult *result, 76 GError **error); 77 78 79G_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 83struct _GFileInputStreamPrivate { 84 GAsyncReadyCallback outstanding_callback; 85}; 86 87static void 88g_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 96static void 97g_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 106static void 107g_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 **/ 130GFileInfo * 131g_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 167static void 168async_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 **/ 203void 204g_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 **/ 248GFileInfo * 249g_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 270static goffset 271g_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 287static goffset 288g_file_input_stream_seekable_tell (GSeekable *seekable) 289{ 290 return g_file_input_stream_tell (G_FILE_INPUT_STREAM (seekable)); 291} 292 293static gboolean 294g_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 314static gboolean 315g_file_input_stream_seekable_can_seek (GSeekable *seekable) 316{ 317 return g_file_input_stream_can_seek (G_FILE_INPUT_STREAM (seekable)); 318} 319 320static gboolean 321g_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 359static gboolean 360g_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 370static gboolean 371g_file_input_stream_seekable_can_truncate (GSeekable *seekable) 372{ 373 return FALSE; 374} 375 376static gboolean 377g_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 391typedef struct { 392 char *attributes; 393 GFileInfo *info; 394} QueryInfoAsyncData; 395 396static void 397query_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 405static void 406query_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 435static void 436g_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 456static GFileInfo * 457g_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