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