1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2012 Jeffrey Stedfast
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1
8 * of the License, or (at your option) any later version.
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.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
34 #include "gmime-stream-file.h"
38 * SECTION: gmime-stream-file
39 * @title: GMimeStreamFile
40 * @short_description: A Standard-C FILE-based stream
41 * @see_also: #GMimeStream
43 * A simple #GMimeStream implementation that sits on top of the
44 * Standard C FILE pointer based I/O layer. Unlike #GMimeStreamFs, a
45 * #GMimeStreamFile will typically buffer read and write operations at
46 * the FILE level and so it may be wasteful to wrap one in a
47 * #GMimeStreamBuffer stream.
51 static void g_mime_stream_file_class_init (GMimeStreamFileClass *klass);
52 static void g_mime_stream_file_init (GMimeStreamFile *stream, GMimeStreamFileClass *klass);
53 static void g_mime_stream_file_finalize (GObject *object);
55 static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
56 static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
57 static int stream_flush (GMimeStream *stream);
58 static int stream_close (GMimeStream *stream);
59 static gboolean stream_eos (GMimeStream *stream);
60 static int stream_reset (GMimeStream *stream);
61 static gint64 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence);
62 static gint64 stream_tell (GMimeStream *stream);
63 static gint64 stream_length (GMimeStream *stream);
64 static GMimeStream *stream_substream (GMimeStream *stream, gint64 start, gint64 end);
67 static GMimeStreamClass *parent_class = NULL;
71 g_mime_stream_file_get_type (void)
73 static GType type = 0;
76 static const GTypeInfo info = {
77 sizeof (GMimeStreamFileClass),
78 NULL, /* base_class_init */
79 NULL, /* base_class_finalize */
80 (GClassInitFunc) g_mime_stream_file_class_init,
81 NULL, /* class_finalize */
82 NULL, /* class_data */
83 sizeof (GMimeStreamFile),
85 (GInstanceInitFunc) g_mime_stream_file_init,
88 type = g_type_register_static (GMIME_TYPE_STREAM, "GMimeStreamFile", &info, 0);
96 g_mime_stream_file_class_init (GMimeStreamFileClass *klass)
98 GMimeStreamClass *stream_class = GMIME_STREAM_CLASS (klass);
99 GObjectClass *object_class = G_OBJECT_CLASS (klass);
101 parent_class = g_type_class_ref (GMIME_TYPE_STREAM);
103 object_class->finalize = g_mime_stream_file_finalize;
105 stream_class->read = stream_read;
106 stream_class->write = stream_write;
107 stream_class->flush = stream_flush;
108 stream_class->close = stream_close;
109 stream_class->eos = stream_eos;
110 stream_class->reset = stream_reset;
111 stream_class->seek = stream_seek;
112 stream_class->tell = stream_tell;
113 stream_class->length = stream_length;
114 stream_class->substream = stream_substream;
118 g_mime_stream_file_init (GMimeStreamFile *stream, GMimeStreamFileClass *klass)
120 stream->owner = TRUE;
125 g_mime_stream_file_finalize (GObject *object)
127 GMimeStreamFile *stream = (GMimeStreamFile *) object;
129 if (stream->owner && stream->fp)
132 G_OBJECT_CLASS (parent_class)->finalize (object);
137 stream_read (GMimeStream *stream, char *buf, size_t len)
139 GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
142 if (fstream->fp == NULL) {
147 if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
152 if (stream->bound_end != -1)
153 len = (size_t) MIN (stream->bound_end - stream->position, (gint64) len);
155 /* make sure we are at the right position */
156 fseek (fstream->fp, (long) stream->position, SEEK_SET);
158 if ((nread = fread (buf, 1, len, fstream->fp)) > 0)
159 stream->position += nread;
161 return (ssize_t) nread;
165 stream_write (GMimeStream *stream, const char *buf, size_t len)
167 GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
170 if (fstream->fp == NULL) {
175 if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
180 if (stream->bound_end != -1)
181 len = (size_t) MIN (stream->bound_end - stream->position, (gint64) len);
183 /* make sure we are at the right position */
184 fseek (fstream->fp, (long) stream->position, SEEK_SET);
186 if ((nwritten = fwrite (buf, 1, len, fstream->fp)) > 0)
187 stream->position += nwritten;
189 return (ssize_t) nwritten;
193 stream_flush (GMimeStream *stream)
195 GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
197 if (fstream->fp == NULL) {
202 return fflush (fstream->fp);
206 stream_close (GMimeStream *stream)
208 GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
211 if (fstream->fp == NULL)
214 if ((rv = fclose (fstream->fp)) != 0)
221 stream_eos (GMimeStream *stream)
223 GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
225 if (fstream->fp == NULL)
228 if (stream->bound_end == -1)
229 return feof (fstream->fp) ? TRUE : FALSE;
231 return stream->position >= stream->bound_end;
235 stream_reset (GMimeStream *stream)
237 GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
239 if (fstream->fp == NULL) {
244 if (stream->position == stream->bound_start)
247 if (fseek (fstream->fp, (long) stream->bound_start, SEEK_SET) == -1)
254 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
256 GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
257 gint64 real = stream->position;
258 FILE *fp = fstream->fp;
260 if (fstream->fp == NULL) {
266 case GMIME_STREAM_SEEK_SET:
269 case GMIME_STREAM_SEEK_CUR:
270 real = stream->position + offset;
272 case GMIME_STREAM_SEEK_END:
273 if (offset > 0 || (stream->bound_end == -1 && !feof (fp))) {
274 /* need to do an actual lseek() here because
275 * we either don't know the offset of the end
276 * of the stream and/or don't know if we can
277 * seek past the end */
278 if (fseek (fp, (long) offset, SEEK_END) == -1 || (real = ftell (fp)) == -1)
280 } else if (feof (fp) && stream->bound_end == -1) {
281 /* seeking backwards from eos (which happens
282 * to be our current position) */
283 real = stream->position + offset;
285 /* seeking backwards from a known position */
286 real = stream->bound_end + offset;
291 /* sanity check the resultant offset */
292 if (real < stream->bound_start) {
297 if (stream->bound_end != -1 && real > stream->bound_end) {
302 if (fseek (fp, (long) real, SEEK_SET) == -1 || (real = ftell (fp)) == -1)
305 stream->position = real;
311 stream_tell (GMimeStream *stream)
313 return stream->position;
317 stream_length (GMimeStream *stream)
319 GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
322 if (fstream->fp == NULL) {
327 if (stream->bound_start != -1 && stream->bound_end != -1)
328 return stream->bound_end - stream->bound_start;
330 fseek (fstream->fp, (long) 0, SEEK_END);
331 bound_end = ftell (fstream->fp);
332 fseek (fstream->fp, (long) stream->position, SEEK_SET);
334 if (bound_end < stream->bound_start) {
339 return bound_end - stream->bound_start;
343 stream_substream (GMimeStream *stream, gint64 start, gint64 end)
345 GMimeStreamFile *fstream;
347 fstream = g_object_newv (GMIME_TYPE_STREAM_FILE, 0, NULL);
348 g_mime_stream_construct (GMIME_STREAM (fstream), start, end);
349 fstream->owner = FALSE;
350 fstream->fp = GMIME_STREAM_FILE (stream)->fp;
352 return GMIME_STREAM (fstream);
357 * g_mime_stream_file_new:
358 * @fp: a FILE pointer
360 * Creates a new #GMimeStreamFile object around @fp.
362 * Note: The created #GMimeStreamFile object will own the FILE pointer
365 * Returns: a stream using @fp.
368 g_mime_stream_file_new (FILE *fp)
370 GMimeStreamFile *fstream;
374 _setmode (_fileno (fp), O_BINARY);
377 if ((start = ftell (fp)) == -1)
380 fstream = g_object_newv (GMIME_TYPE_STREAM_FILE, 0, NULL);
381 g_mime_stream_construct (GMIME_STREAM (fstream), start, -1);
382 fstream->owner = TRUE;
385 return GMIME_STREAM (fstream);
390 * g_mime_stream_file_new_with_bounds:
391 * @fp: a FILE pointer
392 * @start: start boundary
395 * Creates a new #GMimeStreamFile object around @fp with bounds @start
398 * Note: The created #GMimeStreamFile object will own the FILE pointer
401 * Returns: a stream using @fp with bounds @start and @end.
404 g_mime_stream_file_new_with_bounds (FILE *fp, gint64 start, gint64 end)
406 GMimeStreamFile *fstream;
409 _setmode (_fileno (fp), O_BINARY);
412 fstream = g_object_newv (GMIME_TYPE_STREAM_FILE, 0, NULL);
413 g_mime_stream_construct (GMIME_STREAM (fstream), start, end);
414 fstream->owner = TRUE;
417 return GMIME_STREAM (fstream);
422 * g_mime_stream_file_get_owner:
423 * @stream: a #GMimeStreamFile
425 * Gets whether or not @stream owns the backend FILE pointer.
427 * Returns: %TRUE if @stream owns the backend FILE pointer or %FALSE
431 g_mime_stream_file_get_owner (GMimeStreamFile *stream)
433 g_return_val_if_fail (GMIME_IS_STREAM_FILE (stream), FALSE);
435 return stream->owner;
440 * g_mime_stream_file_set_owner:
441 * @stream: a #GMimeStreamFile
442 * @owner: %TRUE if this stream should own the FILE pointer or %FALSE otherwise
444 * Sets whether or not @stream owns the backend FILE pointer.
446 * Note: @owner should be %TRUE if the stream should fclose() the
447 * backend FILE pointer when destroyed or %FALSE otherwise.
450 g_mime_stream_file_set_owner (GMimeStreamFile *stream, gboolean owner)
452 g_return_if_fail (GMIME_IS_STREAM_FILE (stream));
454 stream->owner = owner;