1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
3 /* camel-stream-buffer.c : Buffer any other other stream
5 * Authors: Michael Zucchi <notzed@ximian.com>
7 * Copyright 1999-2003 Ximian, Inc. (www.ximian.com)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of version 2 of the GNU Lesser General Public
11 * License as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
32 #include <sys/types.h>
34 #include "camel-stream-buffer.h"
36 static CamelStreamClass *parent_class = NULL;
39 BUF_USER = 1<<0, /* user-supplied buffer, do not free */
44 static ssize_t stream_read (CamelStream *stream, char *buffer, size_t n);
45 static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n);
46 static int stream_flush (CamelStream *stream);
47 static int stream_close (CamelStream *stream);
48 static gboolean stream_eos (CamelStream *stream);
50 static void init_vbuf(CamelStreamBuffer *sbf, CamelStream *s, CamelStreamBufferMode mode, char *buf, guint32 size);
51 static void init(CamelStreamBuffer *sbuf, CamelStream *s, CamelStreamBufferMode mode);
54 camel_stream_buffer_class_init (CamelStreamBufferClass *camel_stream_buffer_class)
56 CamelStreamClass *camel_stream_class = CAMEL_STREAM_CLASS (camel_stream_buffer_class);
58 parent_class = CAMEL_STREAM_CLASS (camel_type_get_global_classfuncs (camel_stream_get_type ()));
60 /* virtual method definition */
61 camel_stream_buffer_class->init = init;
62 camel_stream_buffer_class->init_vbuf = init_vbuf;
64 /* virtual method overload */
65 camel_stream_class->read = stream_read;
66 camel_stream_class->write = stream_write;
67 camel_stream_class->flush = stream_flush;
68 camel_stream_class->close = stream_close;
69 camel_stream_class->eos = stream_eos;
73 camel_stream_buffer_init (gpointer object, gpointer klass)
75 CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (object);
79 sbf->buf = g_malloc(BUF_SIZE);
82 sbf->mode = CAMEL_STREAM_BUFFER_READ | CAMEL_STREAM_BUFFER_BUFFER;
85 sbf->linebuf = g_malloc(sbf->linesize);
89 camel_stream_buffer_finalize (CamelObject *object)
91 CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (object);
93 if (!(sbf->flags & BUF_USER)) {
97 camel_object_unref (sbf->stream);
104 camel_stream_buffer_get_type (void)
106 static CamelType camel_stream_buffer_type = CAMEL_INVALID_TYPE;
108 if (camel_stream_buffer_type == CAMEL_INVALID_TYPE) {
109 camel_stream_buffer_type = camel_type_register (camel_stream_get_type (), "CamelStreamBuffer",
110 sizeof (CamelStreamBuffer),
111 sizeof (CamelStreamBufferClass),
112 (CamelObjectClassInitFunc) camel_stream_buffer_class_init,
114 (CamelObjectInitFunc) camel_stream_buffer_init,
115 (CamelObjectFinalizeFunc) camel_stream_buffer_finalize);
118 return camel_stream_buffer_type;
123 set_vbuf(CamelStreamBuffer *sbf, char *buf, CamelStreamBufferMode mode, int size)
125 if (sbf->buf && !(sbf->flags & BUF_USER)) {
130 sbf->flags |= BUF_USER;
132 sbf->buf = g_malloc(size);
133 sbf->flags &= ~BUF_USER;
143 init_vbuf(CamelStreamBuffer *sbf, CamelStream *s, CamelStreamBufferMode mode, char *buf, guint32 size)
145 set_vbuf(sbf, buf, mode, size);
147 camel_object_unref (sbf->stream);
149 camel_object_ref (sbf->stream);
153 init(CamelStreamBuffer *sbuf, CamelStream *s, CamelStreamBufferMode mode)
155 init_vbuf(sbuf, s, mode, NULL, BUF_SIZE);
160 * camel_stream_buffer_new:
161 * @stream: a #CamelStream object to buffer
162 * @mode: Operational mode of buffered stream.
164 * Create a new buffered stream of another stream. A default
165 * buffer size (1024 bytes), automatically managed will be used
168 * See #camel_stream_buffer_new_with_vbuf for details on the
171 * Returns a newly created buffered stream.
174 camel_stream_buffer_new (CamelStream *stream, CamelStreamBufferMode mode)
176 CamelStreamBuffer *sbf;
178 sbf = CAMEL_STREAM_BUFFER (camel_object_new (camel_stream_buffer_get_type ()));
179 CAMEL_STREAM_BUFFER_CLASS (CAMEL_OBJECT_GET_CLASS(sbf))->init (sbf, stream, mode);
181 return CAMEL_STREAM (sbf);
185 * camel_stream_buffer_new_with_vbuf:
186 * @stream: An existing stream to buffer.
187 * @mode: Mode to buffer in.
188 * @buf: Memory to use for buffering.
189 * @size: Size of buffer to use.
191 * Create a new stream which buffers another stream, @stream.
193 * The following values are available for @mode:
195 * #CAMEL_STREAM_BUFFER_BUFFER, Buffer the input/output in blocks.
196 * #CAMEL_STREAM_BUFFER_NEWLINE, Buffer on newlines (for output).
197 * #CAMEL_STREAM_BUFFER_NONE, Perform no buffering.
199 * Note that currently this is ignored and #CAMEL_STREAM_BUFFER_BUFFER
202 * In addition, one of the following mode options should be or'd
203 * together with the buffering mode:
205 * #CAMEL_STREAM_BUFFER_WRITE, Buffer in write mode.
206 * #CAMEL_STREAM_BUFFER_READ, Buffer in read mode.
208 * Buffering can only be done in one direction for any
211 * If @buf is non-NULL, then use the memory pointed to
212 * (for upto @size bytes) as the buffer for all buffering
213 * operations. It is upto the application to free this buffer.
214 * If @buf is NULL, then allocate and manage @size bytes
217 * Return value: A new stream with buffering applied.
220 camel_stream_buffer_new_with_vbuf (CamelStream *stream, CamelStreamBufferMode mode, char *buf, guint32 size)
222 CamelStreamBuffer *sbf;
223 sbf = CAMEL_STREAM_BUFFER (camel_object_new (camel_stream_buffer_get_type ()));
224 CAMEL_STREAM_BUFFER_CLASS (CAMEL_OBJECT_GET_CLASS(sbf))->init_vbuf (sbf, stream, mode, buf, size);
226 return CAMEL_STREAM (sbf);
230 stream_read (CamelStream *stream, char *buffer, size_t n)
232 CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
233 ssize_t bytes_read = 1;
237 g_return_val_if_fail( (sbf->mode & CAMEL_STREAM_BUFFER_MODE) == CAMEL_STREAM_BUFFER_READ, 0);
239 while (n && bytes_read > 0) {
240 bytes_left = sbf->end - sbf->ptr;
241 if (bytes_left < n) {
242 if (bytes_left > 0) {
243 memcpy(bptr, sbf->ptr, bytes_left);
246 sbf->ptr += bytes_left;
248 /* if we are reading a lot, then read directly to the destination buffer */
249 if (n >= sbf->size/3) {
250 bytes_read = camel_stream_read(sbf->stream, bptr, n);
256 bytes_read = camel_stream_read(sbf->stream, sbf->buf, sbf->size);
258 size_t bytes_used = bytes_read > n ? n : bytes_read;
260 sbf->end = sbf->buf+bytes_read;
261 memcpy(bptr, sbf->ptr, bytes_used);
262 sbf->ptr += bytes_used;
268 memcpy(bptr, sbf->ptr, n);
275 return (ssize_t)(bptr - buffer);
278 /* only returns the number passed in, or -1 on an error */
280 stream_write_all(CamelStream *stream, const char *buffer, size_t n)
285 w = camel_stream_write(stream, buffer, left);
296 stream_write (CamelStream *stream, const char *buffer, size_t n)
298 CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
302 g_return_val_if_fail( (sbf->mode & CAMEL_STREAM_BUFFER_MODE) == CAMEL_STREAM_BUFFER_WRITE, 0);
304 /* first, copy as much as we can */
305 left = sbf->size - (sbf->ptr-sbf->buf);
308 memcpy(sbf->ptr, buffer, todo);
313 /* if we've filled the buffer, write it out, reset buffer */
315 if (stream_write_all(sbf->stream, sbf->buf, sbf->size) == -1)
321 /* if we still have more, write directly, or copy to buffer */
323 if (n >= sbf->size/3) {
324 if (stream_write_all(sbf->stream, buffer, n) == -1)
327 memcpy(sbf->ptr, buffer, n);
336 stream_flush (CamelStream *stream)
338 CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
340 if ((sbf->mode & CAMEL_STREAM_BUFFER_MODE) == CAMEL_STREAM_BUFFER_WRITE) {
341 size_t len = sbf->ptr - sbf->buf;
343 if (camel_stream_write (sbf->stream, sbf->buf, len) == -1)
348 /* nothing to do for read mode 'flush' */
351 return camel_stream_flush(sbf->stream);
355 stream_close (CamelStream *stream)
357 CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
359 if (stream_flush(stream) == -1)
361 return camel_stream_close(sbf->stream);
365 stream_eos (CamelStream *stream)
367 CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
369 return camel_stream_eos(sbf->stream) && sbf->ptr == sbf->end;
373 * camel_stream_buffer_gets:
374 * @sbf: a #CamelStreamBuffer object
375 * @buf: Memory to write the string to.
376 * @max: Maxmimum number of characters to store.
378 * Read a line of characters up to the next newline character or
381 * If the newline character is encountered, then it will be
382 * included in the buffer @buf. The buffer will be #NUL terminated.
384 * Returns the number of characters read, or %0 for end of file,
388 camel_stream_buffer_gets(CamelStreamBuffer *sbf, char *buf, unsigned int max)
390 register char *outptr, *inptr, *inend, c, *outend;
396 outend = buf+max-1; /* room for NUL */
399 while (inptr<inend && outptr<outend) {
408 if (outptr == outend)
411 bytes_read = camel_stream_read (sbf->stream, sbf->buf, sbf->size);
412 if (bytes_read == -1) {
418 inptr = sbf->ptr = sbf->buf;
419 inend = sbf->end = sbf->buf + bytes_read;
420 } while (bytes_read>0);
425 return (int)(outptr - buf);
429 * camel_stream_buffer_read_line: read a complete line from the stream
430 * @sbf: a #CamelStreamBuffer object
432 * This function reads a complete newline-terminated line from the stream
433 * and returns it in allocated memory. The trailing newline (and carriage
434 * return if any) are not included in the returned string.
436 * Returns the line read, which the caller must free when done with,
437 * or %NULL on eof. If an error occurs, @ex will be set.
440 camel_stream_buffer_read_line (CamelStreamBuffer *sbf)
448 nread = camel_stream_buffer_gets (sbf, p, sbf->linesize - (p - sbf->linebuf));
450 if (p > sbf->linebuf)
459 nread = p - sbf->linebuf;
461 sbf->linebuf = g_realloc (sbf->linebuf, sbf->linesize);
462 p = sbf->linebuf + nread;
466 if (p > sbf->linebuf && p[-1] == '\r')
470 return g_strdup(sbf->linebuf);