gfileinputstream.c revision 3781343738de4abddf56982325a77bd70a98cd26
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 "glibintl.h" 30 31static void g_file_input_stream_seekable_iface_init (GSeekableIface *iface); 32static goffset g_file_input_stream_seekable_tell (GSeekable *seekable); 33static gboolean g_file_input_stream_seekable_can_seek (GSeekable *seekable); 34static gboolean g_file_input_stream_seekable_seek (GSeekable *seekable, 35 goffset offset, 36 GSeekType type, 37 GCancellable *cancellable, 38 GError **error); 39static gboolean g_file_input_stream_seekable_can_truncate (GSeekable *seekable); 40static gboolean g_file_input_stream_seekable_truncate (GSeekable *seekable, 41 goffset offset, 42 GCancellable *cancellable, 43 GError **error); 44static void g_file_input_stream_real_query_info_async (GFileInputStream *stream, 45 char *attributes, 46 int io_priority, 47 GCancellable *cancellable, 48 GAsyncReadyCallback callback, 49 gpointer user_data); 50static GFileInfo *g_file_input_stream_real_query_info_finish (GFileInputStream *stream, 51 GAsyncResult *result, 52 GError **error); 53 54 55G_DEFINE_TYPE_WITH_CODE (GFileInputStream, g_file_input_stream, G_TYPE_INPUT_STREAM, 56 G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, 57 g_file_input_stream_seekable_iface_init)) 58 59struct _GFileInputStreamPrivate { 60 GAsyncReadyCallback outstanding_callback; 61}; 62 63static void 64g_file_input_stream_class_init (GFileInputStreamClass *klass) 65{ 66 g_type_class_add_private (klass, sizeof (GFileInputStreamPrivate)); 67 68 klass->query_info_async = g_file_input_stream_real_query_info_async; 69 klass->query_info_finish = g_file_input_stream_real_query_info_finish; 70} 71 72static void 73g_file_input_stream_seekable_iface_init (GSeekableIface *iface) 74{ 75 iface->tell = g_file_input_stream_seekable_tell; 76 iface->can_seek = g_file_input_stream_seekable_can_seek; 77 iface->seek = g_file_input_stream_seekable_seek; 78 iface->can_truncate = g_file_input_stream_seekable_can_truncate; 79 iface->truncate = g_file_input_stream_seekable_truncate; 80} 81 82static void 83g_file_input_stream_init (GFileInputStream *stream) 84{ 85 stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, 86 G_TYPE_FILE_INPUT_STREAM, 87 GFileInputStreamPrivate); 88} 89 90/** 91 * g_file_input_stream_query_info: 92 * @stream: 93 * @attributes: 94 * @cancellable: optional #GCancellable object, %NULL to ignore. 95 * @error: a #GError location to store the error occuring, or %NULL to 96 * ignore. 97 * Returns: 98 **/ 99GFileInfo * 100g_file_input_stream_query_info (GFileInputStream *stream, 101 char *attributes, 102 GCancellable *cancellable, 103 GError **error) 104{ 105 GFileInputStreamClass *class; 106 GInputStream *input_stream; 107 GFileInfo *info; 108 109 g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL); 110 111 input_stream = G_INPUT_STREAM (stream); 112 113 if (g_input_stream_is_closed (input_stream)) 114 { 115 g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED, 116 _("Stream is already closed")); 117 return NULL; 118 } 119 120 if (g_input_stream_has_pending (input_stream)) 121 { 122 g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING, 123 _("Stream has outstanding operation")); 124 return NULL; 125 } 126 127 info = NULL; 128 129 g_input_stream_set_pending (input_stream, TRUE); 130 131 if (cancellable) 132 g_push_current_cancellable (cancellable); 133 134 class = G_FILE_INPUT_STREAM_GET_CLASS (stream); 135 if (class->query_info) 136 info = class->query_info (stream, attributes, cancellable, error); 137 else 138 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, 139 _("Stream doesn't support query_info")); 140 141 if (cancellable) 142 g_pop_current_cancellable (cancellable); 143 144 g_input_stream_set_pending (input_stream, FALSE); 145 146 return info; 147} 148 149static void 150async_ready_callback_wrapper (GObject *source_object, 151 GAsyncResult *res, 152 gpointer user_data) 153{ 154 GFileInputStream *stream = G_FILE_INPUT_STREAM (source_object); 155 156 g_input_stream_set_pending (G_INPUT_STREAM (stream), FALSE); 157 if (stream->priv->outstanding_callback) 158 (*stream->priv->outstanding_callback) (source_object, res, user_data); 159 g_object_unref (stream); 160} 161 162/** 163 * g_file_input_stream_query_info_async: 164 * @stream: 165 * @attributes: 166 * @io_priority: the io priority of the request. 167 * @cancellable: optional #GCancellable object, %NULL to ignore. @callback: 168 * @user_data: 169 * 170 **/ 171void 172g_file_input_stream_query_info_async (GFileInputStream *stream, 173 char *attributes, 174 int io_priority, 175 GCancellable *cancellable, 176 GAsyncReadyCallback callback, 177 gpointer user_data) 178{ 179 GFileInputStreamClass *klass; 180 GInputStream *input_stream; 181 182 g_return_if_fail (G_IS_FILE_INPUT_STREAM (stream)); 183 184 input_stream = G_INPUT_STREAM (stream); 185 186 if (g_input_stream_is_closed (input_stream)) 187 { 188 g_simple_async_report_error_in_idle (G_OBJECT (stream), 189 callback, 190 user_data, 191 G_IO_ERROR, G_IO_ERROR_CLOSED, 192 _("Stream is already closed")); 193 return; 194 } 195 196 if (g_input_stream_has_pending (input_stream)) 197 { 198 g_simple_async_report_error_in_idle (G_OBJECT (stream), 199 callback, 200 user_data, 201 G_IO_ERROR, G_IO_ERROR_PENDING, 202 _("Stream has outstanding operation")); 203 return; 204 } 205 206 klass = G_FILE_INPUT_STREAM_GET_CLASS (stream); 207 208 g_input_stream_set_pending (input_stream, TRUE); 209 stream->priv->outstanding_callback = callback; 210 g_object_ref (stream); 211 klass->query_info_async (stream, attributes, io_priority, cancellable, 212 async_ready_callback_wrapper, user_data); 213} 214 215/** 216 * g_file_input_stream_query_info_finish: 217 * @stream: 218 * @result: 219 * @error: a #GError location to store the error occuring, or %NULL to 220 * ignore. 221 * Returns: #GFileInfo. 222 **/ 223GFileInfo * 224g_file_input_stream_query_info_finish (GFileInputStream *stream, 225 GAsyncResult *result, 226 GError **error) 227{ 228 GSimpleAsyncResult *simple; 229 GFileInputStreamClass *class; 230 231 g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL); 232 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL); 233 234 if (G_IS_SIMPLE_ASYNC_RESULT (result)) 235 { 236 simple = G_SIMPLE_ASYNC_RESULT (result); 237 if (g_simple_async_result_propagate_error (simple, error)) 238 return NULL; 239 } 240 241 class = G_FILE_INPUT_STREAM_GET_CLASS (stream); 242 return class->query_info_finish (stream, result, error); 243} 244 245/** 246 * g_file_input_stream_tell: 247 * @stream: 248 * 249 * Returns: 250 **/ 251goffset 252g_file_input_stream_tell (GFileInputStream *stream) 253{ 254 GFileInputStreamClass *class; 255 goffset offset; 256 257 g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), 0); 258 259 class = G_FILE_INPUT_STREAM_GET_CLASS (stream); 260 261 offset = 0; 262 if (class->tell) 263 offset = class->tell (stream); 264 265 return offset; 266} 267 268static goffset 269g_file_input_stream_seekable_tell (GSeekable *seekable) 270{ 271 return g_file_input_stream_tell (G_FILE_INPUT_STREAM (seekable)); 272} 273 274/** 275 * g_file_input_stream_can_seek: 276 * @stream: 277 * 278 * Returns: %TRUE if stream can be seeked. %FALSE otherwise. 279 **/ 280gboolean 281g_file_input_stream_can_seek (GFileInputStream *stream) 282{ 283 GFileInputStreamClass *class; 284 gboolean can_seek; 285 286 g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE); 287 288 class = G_FILE_INPUT_STREAM_GET_CLASS (stream); 289 290 can_seek = FALSE; 291 if (class->seek) 292 { 293 can_seek = TRUE; 294 if (class->can_seek) 295 can_seek = class->can_seek (stream); 296 } 297 298 return can_seek; 299} 300 301static gboolean 302g_file_input_stream_seekable_can_seek (GSeekable *seekable) 303{ 304 return g_file_input_stream_can_seek (G_FILE_INPUT_STREAM (seekable)); 305} 306 307/** 308 * g_file_input_stream_seek: 309 * @stream: 310 * @offset: 311 * @type: 312 * @cancellable: optional #GCancellable object, %NULL to ignore. 313 * @error: a #GError location to store the error occuring, or %NULL to 314 * ignore. 315 * Returns: 316 **/ 317gboolean 318g_file_input_stream_seek (GFileInputStream *stream, 319 goffset offset, 320 GSeekType type, 321 GCancellable *cancellable, 322 GError **error) 323{ 324 GFileInputStreamClass *class; 325 GInputStream *input_stream; 326 gboolean res; 327 328 g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE); 329 330 input_stream = G_INPUT_STREAM (stream); 331 class = G_FILE_INPUT_STREAM_GET_CLASS (stream); 332 333 if (g_input_stream_is_closed (input_stream)) 334 { 335 g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED, 336 _("Stream is already closed")); 337 return FALSE; 338 } 339 340 if (g_input_stream_has_pending (input_stream)) 341 { 342 g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING, 343 _("Stream has outstanding operation")); 344 return FALSE; 345 } 346 347 if (!class->seek) 348 { 349 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, 350 _("Seek not supported on stream")); 351 return FALSE; 352 } 353 354 g_input_stream_set_pending (input_stream, TRUE); 355 356 if (cancellable) 357 g_push_current_cancellable (cancellable); 358 359 res = class->seek (stream, offset, type, cancellable, error); 360 361 if (cancellable) 362 g_pop_current_cancellable (cancellable); 363 364 g_input_stream_set_pending (input_stream, FALSE); 365 366 return res; 367} 368 369static gboolean 370g_file_input_stream_seekable_seek (GSeekable *seekable, 371 goffset offset, 372 GSeekType type, 373 GCancellable *cancellable, 374 GError **error) 375{ 376 return g_file_input_stream_seek (G_FILE_INPUT_STREAM (seekable), 377 offset, type, cancellable, error); 378} 379 380static gboolean 381g_file_input_stream_seekable_can_truncate (GSeekable *seekable) 382{ 383 return FALSE; 384} 385 386static gboolean 387g_file_input_stream_seekable_truncate (GSeekable *seekable, 388 goffset offset, 389 GCancellable *cancellable, 390 GError **error) 391{ 392 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, 393 _("Truncate not allowed on input stream")); 394 return FALSE; 395} 396 397/******************************************** 398 * Default implementation of async ops * 399 ********************************************/ 400 401typedef struct { 402 char *attributes; 403 GFileInfo *info; 404} QueryInfoAsyncData; 405 406static void 407query_info_data_free (QueryInfoAsyncData *data) 408{ 409 if (data->info) 410 g_object_unref (data->info); 411 g_free (data->attributes); 412 g_free (data); 413} 414 415static void 416query_info_async_thread (GSimpleAsyncResult *res, 417 GObject *object, 418 GCancellable *cancellable) 419{ 420 GFileInputStreamClass *class; 421 GError *error = NULL; 422 QueryInfoAsyncData *data; 423 GFileInfo *info; 424 425 data = g_simple_async_result_get_op_res_gpointer (res); 426 427 info = NULL; 428 429 class = G_FILE_INPUT_STREAM_GET_CLASS (object); 430 if (class->query_info) 431 info = class->query_info (G_FILE_INPUT_STREAM (object), data->attributes, cancellable, &error); 432 else 433 g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, 434 _("Stream doesn't support query_info")); 435 436 if (info == NULL) 437 { 438 g_simple_async_result_set_from_error (res, error); 439 g_error_free (error); 440 } 441 else 442 data->info = info; 443} 444 445static void 446g_file_input_stream_real_query_info_async (GFileInputStream *stream, 447 char *attributes, 448 int io_priority, 449 GCancellable *cancellable, 450 GAsyncReadyCallback callback, 451 gpointer user_data) 452{ 453 GSimpleAsyncResult *res; 454 QueryInfoAsyncData *data; 455 456 data = g_new0 (QueryInfoAsyncData, 1); 457 data->attributes = g_strdup (attributes); 458 459 res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, g_file_input_stream_real_query_info_async); 460 g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)query_info_data_free); 461 462 g_simple_async_result_run_in_thread (res, query_info_async_thread, io_priority, cancellable); 463 g_object_unref (res); 464} 465 466static GFileInfo * 467g_file_input_stream_real_query_info_finish (GFileInputStream *stream, 468 GAsyncResult *res, 469 GError **error) 470{ 471 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); 472 QueryInfoAsyncData *data; 473 474 g_assert (g_simple_async_result_get_source_tag (simple) == g_file_input_stream_real_query_info_async); 475 476 data = g_simple_async_result_get_op_res_gpointer (simple); 477 if (data->info) 478 return g_object_ref (data->info); 479 480 return NULL; 481} 482