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