• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
55 static void       g_file_input_stream_seekable_iface_init    (GSeekableIface       *iface);
56 static goffset    g_file_input_stream_seekable_tell          (GSeekable            *seekable);
57 static gboolean   g_file_input_stream_seekable_can_seek      (GSeekable            *seekable);
58 static gboolean   g_file_input_stream_seekable_seek          (GSeekable            *seekable,
59 							      goffset               offset,
60 							      GSeekType             type,
61 							      GCancellable         *cancellable,
62 							      GError              **error);
63 static gboolean   g_file_input_stream_seekable_can_truncate  (GSeekable            *seekable);
64 static gboolean   g_file_input_stream_seekable_truncate      (GSeekable            *seekable,
65 							      goffset               offset,
66 							      GCancellable         *cancellable,
67 							      GError              **error);
68 static 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);
74 static GFileInfo *g_file_input_stream_real_query_info_finish (GFileInputStream     *stream,
75 							      GAsyncResult         *result,
76 							      GError              **error);
77 
78 
79 G_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 
83 struct _GFileInputStreamPrivate {
84   GAsyncReadyCallback outstanding_callback;
85 };
86 
87 static void
g_file_input_stream_class_init(GFileInputStreamClass * klass)88 g_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 
96 static void
g_file_input_stream_seekable_iface_init(GSeekableIface * iface)97 g_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 
106 static void
g_file_input_stream_init(GFileInputStream * stream)107 g_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  **/
130 GFileInfo *
g_file_input_stream_query_info(GFileInputStream * stream,const char * attributes,GCancellable * cancellable,GError ** error)131 g_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 
167 static void
async_ready_callback_wrapper(GObject * source_object,GAsyncResult * res,gpointer user_data)168 async_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  **/
203 void
g_file_input_stream_query_info_async(GFileInputStream * stream,const char * attributes,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)204 g_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  **/
248 GFileInfo *
g_file_input_stream_query_info_finish(GFileInputStream * stream,GAsyncResult * result,GError ** error)249 g_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 
270 static goffset
g_file_input_stream_tell(GFileInputStream * stream)271 g_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 
287 static goffset
g_file_input_stream_seekable_tell(GSeekable * seekable)288 g_file_input_stream_seekable_tell (GSeekable *seekable)
289 {
290   return g_file_input_stream_tell (G_FILE_INPUT_STREAM (seekable));
291 }
292 
293 static gboolean
g_file_input_stream_can_seek(GFileInputStream * stream)294 g_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 
314 static gboolean
g_file_input_stream_seekable_can_seek(GSeekable * seekable)315 g_file_input_stream_seekable_can_seek (GSeekable *seekable)
316 {
317   return g_file_input_stream_can_seek (G_FILE_INPUT_STREAM (seekable));
318 }
319 
320 static gboolean
g_file_input_stream_seek(GFileInputStream * stream,goffset offset,GSeekType type,GCancellable * cancellable,GError ** error)321 g_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 
359 static gboolean
g_file_input_stream_seekable_seek(GSeekable * seekable,goffset offset,GSeekType type,GCancellable * cancellable,GError ** error)360 g_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 
370 static gboolean
g_file_input_stream_seekable_can_truncate(GSeekable * seekable)371 g_file_input_stream_seekable_can_truncate (GSeekable *seekable)
372 {
373   return FALSE;
374 }
375 
376 static gboolean
g_file_input_stream_seekable_truncate(GSeekable * seekable,goffset offset,GCancellable * cancellable,GError ** error)377 g_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 
391 typedef struct {
392   char *attributes;
393   GFileInfo *info;
394 } QueryInfoAsyncData;
395 
396 static void
query_info_data_free(QueryInfoAsyncData * data)397 query_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 
405 static void
query_info_async_thread(GSimpleAsyncResult * res,GObject * object,GCancellable * cancellable)406 query_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 
435 static void
g_file_input_stream_real_query_info_async(GFileInputStream * stream,const char * attributes,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)436 g_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 
456 static GFileInfo *
g_file_input_stream_real_query_info_finish(GFileInputStream * stream,GAsyncResult * res,GError ** error)457 g_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