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