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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
32
#include <sys/types.h>
34
#include "camel-stream-buffer.h"
35
#include "camel-tcp-stream.h"
37
static CamelStreamClass *parent_class = NULL;
40
BUF_USER = 1<<0, /* user-supplied buffer, do not free */
45
static ssize_t stream_read (CamelStream *stream, char *buffer, size_t n);
46
static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n);
47
static int stream_flush (CamelStream *stream);
48
static int stream_close (CamelStream *stream);
49
static gboolean stream_eos (CamelStream *stream);
51
static void init_vbuf(CamelStreamBuffer *sbf, CamelStream *s, CamelStreamBufferMode mode, char *buf, guint32 size);
52
static void init(CamelStreamBuffer *sbuf, CamelStream *s, CamelStreamBufferMode mode);
55
camel_stream_buffer_class_init (CamelStreamBufferClass *camel_stream_buffer_class)
57
CamelStreamClass *camel_stream_class = CAMEL_STREAM_CLASS (camel_stream_buffer_class);
59
parent_class = CAMEL_STREAM_CLASS (camel_type_get_global_classfuncs (camel_stream_get_type ()));
61
/* virtual method definition */
62
camel_stream_buffer_class->init = init;
63
camel_stream_buffer_class->init_vbuf = init_vbuf;
65
/* virtual method overload */
66
camel_stream_class->read = stream_read;
67
camel_stream_class->write = stream_write;
68
camel_stream_class->flush = stream_flush;
69
camel_stream_class->close = stream_close;
70
camel_stream_class->eos = stream_eos;
74
camel_stream_buffer_init (gpointer object, gpointer klass)
76
CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (object);
80
sbf->buf = g_malloc(BUF_SIZE);
83
sbf->mode = CAMEL_STREAM_BUFFER_READ | CAMEL_STREAM_BUFFER_BUFFER;
86
sbf->linebuf = g_malloc(sbf->linesize);
90
camel_stream_buffer_finalize (CamelObject *object)
92
CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (object);
94
if (!(sbf->flags & BUF_USER)) {
98
camel_object_unref (sbf->stream);
100
g_free(sbf->linebuf);
105
camel_stream_buffer_get_type (void)
107
static CamelType camel_stream_buffer_type = CAMEL_INVALID_TYPE;
109
if (camel_stream_buffer_type == CAMEL_INVALID_TYPE) {
110
camel_stream_buffer_type = camel_type_register (camel_stream_get_type (), "CamelStreamBuffer",
111
sizeof (CamelStreamBuffer),
112
sizeof (CamelStreamBufferClass),
113
(CamelObjectClassInitFunc) camel_stream_buffer_class_init,
115
(CamelObjectInitFunc) camel_stream_buffer_init,
116
(CamelObjectFinalizeFunc) camel_stream_buffer_finalize);
119
return camel_stream_buffer_type;
124
set_vbuf(CamelStreamBuffer *sbf, char *buf, CamelStreamBufferMode mode, int size)
126
if (sbf->buf && !(sbf->flags & BUF_USER)) {
130
sbf->buf = (unsigned char*)buf;
131
sbf->flags |= BUF_USER;
133
sbf->buf = g_malloc(size);
134
sbf->flags &= ~BUF_USER;
144
init_vbuf(CamelStreamBuffer *sbf, CamelStream *s, CamelStreamBufferMode mode, char *buf, guint32 size)
146
set_vbuf(sbf, buf, mode, size);
148
camel_object_unref (sbf->stream);
150
camel_object_ref (sbf->stream);
154
init(CamelStreamBuffer *sbuf, CamelStream *s, CamelStreamBufferMode mode)
156
init_vbuf(sbuf, s, mode, NULL, BUF_SIZE);
161
* camel_stream_buffer_new:
162
* @stream: a #CamelStream object to buffer
163
* @mode: Operational mode of buffered stream.
165
* Create a new buffered stream of another stream. A default
166
* buffer size (1024 bytes), automatically managed will be used
169
* See #camel_stream_buffer_new_with_vbuf for details on the
172
* Returns a newly created buffered stream.
175
camel_stream_buffer_new (CamelStream *stream, CamelStreamBufferMode mode)
177
CamelStreamBuffer *sbf;
179
sbf = CAMEL_STREAM_BUFFER (camel_object_new (camel_stream_buffer_get_type ()));
180
CAMEL_STREAM_BUFFER_CLASS (CAMEL_OBJECT_GET_CLASS(sbf))->init (sbf, stream, mode);
182
return CAMEL_STREAM (sbf);
186
* camel_stream_buffer_new_with_vbuf:
187
* @stream: An existing stream to buffer.
188
* @mode: Mode to buffer in.
189
* @buf: Memory to use for buffering.
190
* @size: Size of buffer to use.
192
* Create a new stream which buffers another stream, @stream.
194
* The following values are available for @mode:
196
* #CAMEL_STREAM_BUFFER_BUFFER, Buffer the input/output in blocks.
197
* #CAMEL_STREAM_BUFFER_NEWLINE, Buffer on newlines (for output).
198
* #CAMEL_STREAM_BUFFER_NONE, Perform no buffering.
200
* Note that currently this is ignored and #CAMEL_STREAM_BUFFER_BUFFER
203
* In addition, one of the following mode options should be or'd
204
* together with the buffering mode:
206
* #CAMEL_STREAM_BUFFER_WRITE, Buffer in write mode.
207
* #CAMEL_STREAM_BUFFER_READ, Buffer in read mode.
209
* Buffering can only be done in one direction for any
212
* If @buf is non-NULL, then use the memory pointed to
213
* (for upto @size bytes) as the buffer for all buffering
214
* operations. It is upto the application to free this buffer.
215
* If @buf is NULL, then allocate and manage @size bytes
218
* Return value: A new stream with buffering applied.
221
camel_stream_buffer_new_with_vbuf (CamelStream *stream, CamelStreamBufferMode mode, char *buf, guint32 size)
223
CamelStreamBuffer *sbf;
224
sbf = CAMEL_STREAM_BUFFER (camel_object_new (camel_stream_buffer_get_type ()));
225
CAMEL_STREAM_BUFFER_CLASS (CAMEL_OBJECT_GET_CLASS(sbf))->init_vbuf (sbf, stream, mode, buf, size);
227
return CAMEL_STREAM (sbf);
231
stream_read (CamelStream *stream, char *buffer, size_t n)
233
CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
234
ssize_t bytes_read = 1;
238
g_return_val_if_fail( (sbf->mode & CAMEL_STREAM_BUFFER_MODE) == CAMEL_STREAM_BUFFER_READ, 0);
240
while (n && bytes_read > 0) {
241
bytes_left = sbf->end - sbf->ptr;
242
if (bytes_left < n) {
243
if (bytes_left > 0) {
244
memcpy(bptr, sbf->ptr, bytes_left);
247
sbf->ptr += bytes_left;
249
/* if we are reading a lot, then read directly to the destination buffer */
250
if (n >= sbf->size/3) {
251
bytes_read = camel_stream_read(sbf->stream, bptr, n);
257
bytes_read = camel_stream_read(sbf->stream, (char *)sbf->buf, sbf->size);
259
size_t bytes_used = bytes_read > n ? n : bytes_read;
261
sbf->end = sbf->buf+bytes_read;
262
memcpy(bptr, sbf->ptr, bytes_used);
263
sbf->ptr += bytes_used;
269
memcpy(bptr, sbf->ptr, n);
276
return (ssize_t)(bptr - buffer);
279
/* only returns the number passed in, or -1 on an error */
281
stream_write_all(CamelStream *stream, const char *buffer, size_t n)
286
w = camel_stream_write(stream, buffer, left);
297
stream_write (CamelStream *stream, const char *buffer, size_t n)
299
CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
303
g_return_val_if_fail( (sbf->mode & CAMEL_STREAM_BUFFER_MODE) == CAMEL_STREAM_BUFFER_WRITE, 0);
305
/* first, copy as much as we can */
306
left = sbf->size - (sbf->ptr-sbf->buf);
309
memcpy(sbf->ptr, buffer, todo);
314
/* if we've filled the buffer, write it out, reset buffer */
316
if (stream_write_all(sbf->stream, (const char *)sbf->buf, sbf->size) == -1)
322
/* if we still have more, write directly, or copy to buffer */
324
if (n >= sbf->size/3) {
325
if (stream_write_all(sbf->stream, buffer, n) == -1)
328
memcpy(sbf->ptr, buffer, n);
337
stream_flush (CamelStream *stream)
339
CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
341
if ((sbf->mode & CAMEL_STREAM_BUFFER_MODE) == CAMEL_STREAM_BUFFER_WRITE) {
342
size_t len = sbf->ptr - sbf->buf;
344
if (camel_stream_write (sbf->stream, (const char *)sbf->buf, len) == -1)
349
/* nothing to do for read mode 'flush' */
352
return camel_stream_flush(sbf->stream);
356
stream_close (CamelStream *stream)
358
CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
360
if (stream_flush(stream) == -1)
362
return camel_stream_close(sbf->stream);
366
stream_eos (CamelStream *stream)
368
CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
370
return camel_stream_eos(sbf->stream) && sbf->ptr == sbf->end;
374
* camel_stream_buffer_gets:
375
* @sbf: a #CamelStreamBuffer object
376
* @buf: Memory to write the string to.
377
* @max: Maxmimum number of characters to store.
379
* Read a line of characters up to the next newline character or
382
* If the newline character is encountered, then it will be
383
* included in the buffer @buf. The buffer will be #NUL terminated.
385
* Returns the number of characters read, or %0 for end of file,
389
camel_stream_buffer_gets(CamelStreamBuffer *sbf, char *buf, unsigned int max)
391
register char *outptr, *inptr, *inend, c, *outend;
395
inptr = (char*)sbf->ptr;
396
inend = (char*)sbf->end;
397
outend = buf+max-1; /* room for NUL */
400
while (inptr<inend && outptr<outend) {
405
sbf->ptr = (unsigned char*) inptr;
409
if (outptr == outend)
412
bytes_read = camel_stream_read (sbf->stream, (char*)sbf->buf, sbf->size);
413
if (bytes_read == -1) {
420
sbf->end = sbf->buf + bytes_read;
421
inptr = (char*)sbf->ptr;
422
inend = (char*)sbf->end;
423
} while (bytes_read>0);
425
sbf->ptr = (unsigned char*)inptr;
428
return (int)(outptr - buf);
434
camel_stream_buffer_gets_idle (CamelStreamBuffer *sbf, char *buf, unsigned int max)
436
register char *outptr, *inptr, *inend, c, *outend;
440
inptr = (char*)sbf->ptr;
441
inend = (char*)sbf->end;
442
outend = buf+max-1; /* room for NUL */
445
while (inptr<inend && outptr<outend) {
450
sbf->ptr = (unsigned char*) inptr;
454
if (outptr == outend)
457
bytes_read = camel_stream_read_idle (sbf->stream, (char*)sbf->buf, sbf->size);
458
if (bytes_read == -1) {
465
sbf->end = sbf->buf + bytes_read;
466
inptr = (char*)sbf->ptr;
467
inend = (char*)sbf->end;
468
} while (bytes_read>0);
470
sbf->ptr = (unsigned char*)inptr;
473
return (int)(outptr - buf);
477
camel_tcp_stream_buffer_gets_nb (CamelStreamBuffer *sbf, char *buf, unsigned int max)
479
register char *outptr, *inptr, *inend, c, *outend;
483
inptr = (char*)sbf->ptr;
484
inend = (char*)sbf->end;
485
outend = buf+max-1; /* room for NUL */
488
while (inptr<inend && outptr<outend) {
493
sbf->ptr = (unsigned char*) inptr;
497
if (outptr == outend)
500
bytes_read = camel_tcp_stream_read_nb ((CamelTcpStream *)sbf->stream, (char*)sbf->buf, sbf->size);
501
if (bytes_read == -1) {
508
sbf->end = sbf->buf + bytes_read;
509
inptr = (char*)sbf->ptr;
510
inend = (char*)sbf->end;
511
} while (bytes_read>0);
513
sbf->ptr = (unsigned char*)inptr;
516
return (int)(outptr - buf);
520
* camel_stream_buffer_read_line: read a complete line from the stream
521
* @sbf: a #CamelStreamBuffer object
523
* This function reads a complete newline-terminated line from the stream
524
* and returns it in allocated memory. The trailing newline (and carriage
525
* return if any) are not included in the returned string.
527
* Returns the line read, which the caller must free when done with,
528
* or %NULL on eof. If an error occurs, @ex will be set.
531
camel_stream_buffer_read_line (CamelStreamBuffer *sbf)
539
nread = camel_stream_buffer_gets (sbf, (char*)p, sbf->linesize - (p - sbf->linebuf));
541
if (p > sbf->linebuf)
550
nread = p - sbf->linebuf;
552
sbf->linebuf = g_realloc (sbf->linebuf, sbf->linesize);
553
p = sbf->linebuf + nread;
557
if (p > sbf->linebuf && p[-1] == '\r')
561
return g_strdup((gchar*)sbf->linebuf);