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