Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-stream-buffer.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 Jeffrey Stedfast
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 License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  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 Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <errno.h>
28
29 #include "gmime-stream-buffer.h"
30
31 /**
32  * SECTION: gmime-stream-buffer
33  * @title: GMimeStreamBuffer
34  * @short_description: A buffered stream
35  * @see_also: #GMimeStream
36  *
37  * A #GMimeStreamBuffer can be used on top of any other type of stream
38  * and has 3 modes: block reads, block writes, and cached reads. Block
39  * reads are especially useful if you will be making a lot of small
40  * reads from a stream that accesses the file system. Block writes are
41  * useful for very much the same reason. The final mode, cached reads,
42  * can become memory intensive but can be very helpful when inheriting
43  * from a stream that does not support seeking (Note: this mode is the
44  * least tested so be careful using it).
45  **/
46
47 #define BLOCK_BUFFER_LEN   4096
48 #define BUFFER_GROW_SIZE   1024  /* should this also be 4k? */
49
50 static void g_mime_stream_buffer_class_init (GMimeStreamBufferClass *klass);
51 static void g_mime_stream_buffer_init (GMimeStreamBuffer *stream, GMimeStreamBufferClass *klass);
52 static void g_mime_stream_buffer_finalize (GObject *object);
53
54 static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
55 static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
56 static int stream_flush (GMimeStream *stream);
57 static int stream_close (GMimeStream *stream);
58 static gboolean stream_eos (GMimeStream *stream);
59 static int stream_reset (GMimeStream *stream);
60 static gint64 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence);
61 static gint64 stream_tell (GMimeStream *stream);
62 static gint64 stream_length (GMimeStream *stream);
63 static GMimeStream *stream_substream (GMimeStream *stream, gint64 start, gint64 end);
64
65
66 static GMimeStreamClass *parent_class = NULL;
67
68
69 GType
70 g_mime_stream_buffer_get_type (void)
71 {
72         static GType type = 0;
73         
74         if (!type) {
75                 static const GTypeInfo info = {
76                         sizeof (GMimeStreamBufferClass),
77                         NULL, /* base_class_init */
78                         NULL, /* base_class_finalize */
79                         (GClassInitFunc) g_mime_stream_buffer_class_init,
80                         NULL, /* class_finalize */
81                         NULL, /* class_data */
82                         sizeof (GMimeStreamBuffer),
83                         0,    /* n_preallocs */
84                         (GInstanceInitFunc) g_mime_stream_buffer_init,
85                 };
86                 
87                 type = g_type_register_static (GMIME_TYPE_STREAM, "GMimeStreamBuffer", &info, 0);
88         }
89         
90         return type;
91 }
92
93
94 static void
95 g_mime_stream_buffer_class_init (GMimeStreamBufferClass *klass)
96 {
97         GMimeStreamClass *stream_class = GMIME_STREAM_CLASS (klass);
98         GObjectClass *object_class = G_OBJECT_CLASS (klass);
99         
100         parent_class = g_type_class_ref (GMIME_TYPE_STREAM);
101         
102         object_class->finalize = g_mime_stream_buffer_finalize;
103         
104         stream_class->read = stream_read;
105         stream_class->write = stream_write;
106         stream_class->flush = stream_flush;
107         stream_class->close = stream_close;
108         stream_class->eos = stream_eos;
109         stream_class->reset = stream_reset;
110         stream_class->seek = stream_seek;
111         stream_class->tell = stream_tell;
112         stream_class->length = stream_length;
113         stream_class->substream = stream_substream;
114 }
115
116 static void
117 g_mime_stream_buffer_init (GMimeStreamBuffer *stream, GMimeStreamBufferClass *klass)
118 {
119         stream->source = NULL;
120         stream->buffer = NULL;
121         stream->bufptr = NULL;
122         stream->bufend = NULL;
123         stream->buflen = 0;
124         stream->mode = 0;
125 }
126
127 static void
128 g_mime_stream_buffer_finalize (GObject *object)
129 {
130         GMimeStreamBuffer *stream = (GMimeStreamBuffer *) object;
131         
132         if (stream->source)
133                 g_object_unref (stream->source);
134         
135         g_free (stream->buffer);
136         
137         G_OBJECT_CLASS (parent_class)->finalize (object);
138 }
139
140
141 static ssize_t
142 stream_read (GMimeStream *stream, char *buf, size_t len)
143 {
144         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
145         size_t buflen, offset;
146         ssize_t n, nread = 0;
147         
148         if (buffer->source == NULL) {
149                 errno = EBADF;
150                 return -1;
151         }
152         
153         switch (buffer->mode) {
154         case GMIME_STREAM_BUFFER_BLOCK_READ:
155                 while (len > 0) {
156                         /* consume what we can from any pre-buffered data we have left */
157                         if ((n = MIN (buffer->buflen, len)) > 0) {
158                                 memcpy (buf + nread, buffer->bufptr, n);
159                                 buffer->bufptr += n;
160                                 buffer->buflen -= n;
161                                 nread += n;
162                                 len -= n;
163                         }
164                         
165                         if (len >= BLOCK_BUFFER_LEN) {
166                                 /* bypass intermediate buffer, read straight from disk */
167                                 buffer->bufptr = buffer->buffer;
168                                 if ((n = g_mime_stream_read (buffer->source, buf + nread, len)) > 0) {
169                                         nread += n;
170                                         len -= n;
171                                 }
172                                 
173                                 break;
174                         } else if (len > 0) {
175                                 /* buffer more data */
176                                 if ((n = g_mime_stream_read (buffer->source, buffer->buffer, BLOCK_BUFFER_LEN)) > 0)
177                                         buffer->buflen = n;
178                                 
179                                 buffer->bufptr = buffer->buffer;
180                         }
181                         
182                         if (n <= 0) {
183                                 if (nread == 0)
184                                         return n;
185                                 
186                                 break;
187                         }
188                 }
189                 break;
190         case GMIME_STREAM_BUFFER_CACHE_READ:
191                 while (len > 0) {
192                         buflen = (size_t) (buffer->bufend - buffer->bufptr);
193                         if ((n = MIN (buflen, len)) > 0) {
194                                 memcpy (buf + nread, buffer->bufptr, n);
195                                 buffer->bufptr += n;
196                                 nread += n;
197                                 len -= n;
198                         }
199                         
200                         if (len > 0) {
201                                 /* we need to read more data... */
202                                 offset = buffer->bufptr - buffer->buffer;
203                                 
204                                 buffer->buflen = buffer->bufend - buffer->buffer + MAX (BUFFER_GROW_SIZE, len);
205                                 buffer->buffer = g_realloc (buffer->buffer, buffer->buflen);
206                                 buffer->bufend = buffer->buffer + buffer->buflen;
207                                 buffer->bufptr = buffer->buffer + offset;
208                                 
209                                 n = g_mime_stream_read (buffer->source, buffer->bufptr,
210                                                         buffer->bufend - buffer->bufptr);
211                                 
212                                 buffer->bufend = n > 0 ? buffer->bufptr + n : buffer->bufptr;
213                                 
214                                 if (n <= 0) {
215                                         if (nread == 0)
216                                                 return n;
217                                         
218                                         break;
219                                 }
220                         }
221                 }
222                 break;
223         default:
224                 if ((nread = g_mime_stream_read (buffer->source, buf, len)) == -1)
225                         return -1;
226                 
227                 break;
228         }
229         
230         stream->position += nread;
231         
232         return nread;
233 }
234
235 static ssize_t
236 stream_write (GMimeStream *stream, const char *buf, size_t len)
237 {
238         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
239         GMimeStream *source = buffer->source;
240         ssize_t n, nwritten = 0;
241         size_t left = len;
242         
243         if (buffer->source == NULL) {
244                 errno = EBADF;
245                 return -1;
246         }
247         
248         switch (buffer->mode) {
249         case GMIME_STREAM_BUFFER_BLOCK_WRITE:
250                 while (left > 0) {
251                         n = MIN (BLOCK_BUFFER_LEN - buffer->buflen, left);
252                         if (buffer->buflen > 0 || n < BLOCK_BUFFER_LEN) {
253                                 /* add the data to our pending write buffer */
254                                 memcpy (buffer->bufptr, buf + nwritten, n);
255                                 buffer->bufptr += n;
256                                 buffer->buflen += n;
257                                 nwritten += n;
258                                 left -= n;
259                         }
260                         
261                         if (buffer->buflen == BLOCK_BUFFER_LEN) {
262                                 /* flush our buffer... */
263                                 n = g_mime_stream_write (source, buffer->buffer, BLOCK_BUFFER_LEN);
264                                 if (n == BLOCK_BUFFER_LEN) {
265                                         /* wrote everything... */
266                                         buffer->bufptr = buffer->buffer;
267                                         buffer->buflen = 0;
268                                 } else if (n > 0) {
269                                         /* still have buffered data left... */
270                                         memmove (buffer->buffer, buffer->buffer + n, BLOCK_BUFFER_LEN - n);
271                                         buffer->bufptr -= n;
272                                         buffer->buflen -= n;
273                                 } else if (n == -1) {
274                                         if (nwritten == 0)
275                                                 return -1;
276                                         
277                                         break;
278                                 }
279                         }
280                         
281                         if (buffer->buflen == 0 && left >= BLOCK_BUFFER_LEN) {
282                                 while (left >= BLOCK_BUFFER_LEN) {
283                                         if ((n = g_mime_stream_write (source, buf + nwritten, BLOCK_BUFFER_LEN)) == -1) {
284                                                 if (nwritten == 0)
285                                                         return -1;
286                                                 
287                                                 break;
288                                         }
289                                         
290                                         nwritten += n;
291                                         left -= n;
292                                 }
293                                 
294                                 if (left >= BLOCK_BUFFER_LEN)
295                                         break;
296                         }
297                 }
298                 break;
299         default:
300                 if ((nwritten = g_mime_stream_write (source, buf, len)) == -1)
301                         return -1;
302                 
303                 break;
304         }
305         
306         stream->position += nwritten;
307         
308         return nwritten;
309 }
310
311 static int
312 stream_flush (GMimeStream *stream)
313 {
314         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
315         
316         if (buffer->mode == GMIME_STREAM_BUFFER_BLOCK_WRITE && buffer->buflen > 0) {
317                 ssize_t written = 0;
318                 
319                 written = g_mime_stream_write (buffer->source, buffer->buffer, buffer->buflen);
320                 if (written > 0) {
321                         memmove (buffer->buffer, buffer->buffer + written, buffer->buflen - written);
322                         buffer->bufptr -= written;
323                         buffer->buflen -= written;
324                 }
325                 
326                 if (buffer->buflen != 0)
327                         return -1;
328         }
329         
330         return g_mime_stream_flush (buffer->source);
331 }
332
333 static int
334 stream_close (GMimeStream *stream)
335 {
336         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
337         
338         if (buffer->source == NULL)
339                 return 0;
340         
341         g_mime_stream_close (buffer->source);
342         g_object_unref (buffer->source);
343         buffer->source = NULL;
344         
345         g_free (buffer->buffer);
346         buffer->buffer = NULL;
347         buffer->bufptr = NULL;
348         buffer->bufend = NULL;
349         buffer->buflen = 0;
350         
351         return 0;
352 }
353
354 static gboolean
355 stream_eos (GMimeStream *stream)
356 {
357         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
358         
359         if (buffer->source == NULL)
360                 return TRUE;
361         
362         if (!g_mime_stream_eos (buffer->source))
363                 return FALSE;
364         
365         switch (buffer->mode) {
366         case GMIME_STREAM_BUFFER_BLOCK_READ:
367                 return buffer->buflen == 0;
368         case GMIME_STREAM_BUFFER_CACHE_READ:
369                 return buffer->bufptr == buffer->bufend;
370         default:
371                 break;
372         }
373         
374         return TRUE;
375 }
376
377 static int
378 stream_reset (GMimeStream *stream)
379 {
380         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
381         
382         if (buffer->source == NULL) {
383                 errno = EBADF;
384                 return -1;
385         }
386         
387         switch (buffer->mode) {
388         case GMIME_STREAM_BUFFER_BLOCK_READ:
389         case GMIME_STREAM_BUFFER_BLOCK_WRITE:
390                 if (g_mime_stream_reset (buffer->source) == -1)
391                         return -1;
392                 
393                 buffer->bufptr = buffer->buffer;
394                 buffer->buflen = 0;
395                 break;
396         case GMIME_STREAM_BUFFER_CACHE_READ:
397                 buffer->bufptr = buffer->buffer;
398                 break;
399         default:
400                 if (g_mime_stream_reset (buffer->source) == -1)
401                         return -1;
402                 
403                 break;
404         }
405         
406         return 0;
407 }
408
409 static gint64
410 stream_seek_block_read (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
411 {
412         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
413         gint64 real;
414         
415         /* convert all seeks into a relative seek (aka SEEK_CUR) */
416         switch (whence) {
417         case GMIME_STREAM_SEEK_CUR:
418                 /* no-op for now */
419                 break;
420         case GMIME_STREAM_SEEK_SET:
421                 if (stream->position == offset)
422                         return stream->position;
423                 
424                 if (offset < 0) {
425                         /* not allowed to seek to a negative position */
426                         errno = EINVAL;
427                         return -1;
428                 }
429                 
430                 offset -= stream->position;
431                 break;
432         case GMIME_STREAM_SEEK_END:
433                 if (stream->bound_end == -1) {
434                         /* gotta do this the slow way */
435                         if ((real = g_mime_stream_seek (buffer->source, offset, GMIME_STREAM_SEEK_END) == -1))
436                                 return -1;
437                         
438                         stream->position = real;
439                         
440                         buffer->bufptr = buffer->buffer;
441                         buffer->buflen = 0;
442                         
443                         return real;
444                 }
445                 
446                 if (offset > 0) {
447                         /* not allowed to seek past bound_end */
448                         errno = EINVAL;
449                         return -1;
450                 }
451                 
452                 offset += stream->bound_end;
453                 break;
454         default:
455                 /* invalid whence argument */
456                 errno = EINVAL;
457                 return -1;
458         }
459         
460         /* now that offset is relative to our current position... */
461         
462         if (offset == 0)
463                 return stream->position;
464         
465         if ((offset < 0 && offset >= (buffer->buffer - buffer->bufptr))
466             || (offset > 0 && offset <= buffer->buflen)) {
467                 /* the position is within our pre-buffered region */
468                 stream->position += offset;
469                 buffer->bufptr += (size_t) offset;
470                 buffer->buflen -= (size_t) offset;
471                 
472                 return stream->position;
473         }
474         
475         /* we are now forced to do an actual seek */
476         offset += stream->position;
477         if ((real = g_mime_stream_seek (buffer->source, offset, GMIME_STREAM_SEEK_SET)) == -1)
478                 return -1;
479         
480         stream->position = real;
481         
482         buffer->bufptr = buffer->buffer;
483         buffer->buflen = 0;
484         
485         return real;
486 }
487
488 static gint64
489 stream_seek_cache_read (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
490 {
491         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
492         gint64 buflen, len, total = 0;
493         gint64 pos, real;
494         ssize_t nread;
495         
496         switch (whence) {
497         case GMIME_STREAM_SEEK_SET:
498                 real = offset;
499                 break;
500         case GMIME_STREAM_SEEK_CUR:
501                 real = stream->position + offset;
502                 break;
503         case GMIME_STREAM_SEEK_END:
504                 if (stream->bound_end == -1) {
505                         /* we have to do a real seek because the end boundary is unknown */
506                         if ((real = g_mime_stream_seek (buffer->source, offset, whence)) == -1)
507                                 return -1;
508                         
509                         if (real < stream->bound_start) {
510                                 /* seek offset out of bounds */
511                                 errno = EINVAL;
512                                 return -1;
513                         }
514                 } else {
515                         real = stream->bound_end + offset;
516                         if (real > stream->bound_end || real < stream->bound_start) {
517                                 /* seek offset out of bounds */
518                                 errno = EINVAL;
519                                 return -1;
520                         }
521                 }
522                 break;
523         default:
524                 /* invalid whence argument */
525                 errno = EINVAL;
526                 return -1;
527         }
528         
529         if (real > stream->position) {
530                 /* buffer any data between position and real */
531                 len = real - (stream->bound_start + (buffer->bufend - buffer->bufptr));
532                 
533                 if (buffer->bufptr + len <= buffer->bufend) {
534                         buffer->bufptr += len;
535                         stream->position = real;
536                         return real;
537                 }
538                 
539                 pos = buffer->bufptr - buffer->buffer;
540                 
541                 buflen = (buffer->bufend - buffer->buffer) + len;
542                 if (buflen < G_MAXSIZE)
543                         buffer->buflen = (size_t) buflen;
544                 else
545                         buffer->buflen = G_MAXSIZE;
546                 
547                 buffer->buffer = g_realloc (buffer->buffer, buffer->buflen);
548                 buffer->bufend = buffer->buffer + buffer->buflen;
549                 buffer->bufptr = buffer->buffer + pos;
550                 
551                 do {
552                         nread = g_mime_stream_read (buffer->source, buffer->bufptr,
553                                                     buffer->bufend - buffer->bufptr);
554                         if (nread > 0) {
555                                 total += nread;
556                                 buffer->bufptr += nread;
557                         }
558                 } while (nread != -1);
559                 
560                 buffer->bufend = buffer->bufptr;
561                 if (total < len) {
562                         /* we failed to seek that far so reset our bufptr */
563                         buffer->bufptr = buffer->buffer + pos;
564                         errno = EINVAL;
565                         return -1;
566                 }
567         } else if (real < stream->bound_start) {
568                 /* seek offset out of bounds */
569                 errno = EINVAL;
570                 return -1;
571         } else {
572                 /* seek our cache pointer backwards */
573                 buffer->bufptr = buffer->buffer + (real - stream->bound_start);
574         }
575         
576         stream->position = real;
577         
578         return real;
579 }
580
581 static gint64
582 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
583 {
584         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
585         gint64 real;
586         
587         if (buffer->source == NULL) {
588                 errno = EBADF;
589                 return -1;
590         }
591         
592         switch (buffer->mode) {
593         case GMIME_STREAM_BUFFER_BLOCK_WRITE:
594                 if (stream_flush (stream) != 0)
595                         return -1;
596                 
597                 if ((real = g_mime_stream_seek (buffer->source, offset, whence)) != -1) {
598                         stream->position = real;
599                         buffer->buflen = 0;
600                 }
601                 
602                 return real;
603         case GMIME_STREAM_BUFFER_BLOCK_READ:
604                 return stream_seek_block_read (stream, offset, whence);
605         case GMIME_STREAM_BUFFER_CACHE_READ:
606                 return stream_seek_cache_read (stream, offset, whence);
607         default:
608                 /* invalid whence argument */
609                 errno = EINVAL;
610                 return -1;
611         }
612 }
613
614 static gint64
615 stream_tell (GMimeStream *stream)
616 {
617         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
618         
619         if (buffer->source == NULL) {
620                 errno = EBADF;
621                 return -1;
622         }
623         
624         return stream->position;
625 }
626
627 static gint64
628 stream_length (GMimeStream *stream)
629 {
630         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
631         
632         if (buffer->source == NULL) {
633                 errno = EBADF;
634                 return -1;
635         }
636         
637         return g_mime_stream_length (buffer->source);
638 }
639
640 static GMimeStream *
641 stream_substream (GMimeStream *stream, gint64 start, gint64 end)
642 {
643         GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
644         
645         /* FIXME: for cached reads we want to substream ourself rather
646            than substreaming our source because we have to assume that
647            the reason this stream is setup to do cached reads is
648            because the source stream is unseekable. */
649         
650         return GMIME_STREAM_GET_CLASS (buffer->source)->substream (buffer->source, start, end);
651 }
652
653
654 /**
655  * g_mime_stream_buffer_new:
656  * @source: source stream
657  * @mode: buffering mode
658  *
659  * Creates a new GMimeStreamBuffer object.
660  *
661  * Returns: a new buffer stream with source @source and mode @mode.
662  **/
663 GMimeStream *
664 g_mime_stream_buffer_new (GMimeStream *source, GMimeStreamBufferMode mode)
665 {
666         GMimeStreamBuffer *buffer;
667         
668         g_return_val_if_fail (GMIME_IS_STREAM (source), NULL);
669         
670         buffer = g_object_newv (GMIME_TYPE_STREAM_BUFFER, 0, NULL);
671         
672         buffer->source = source;
673         g_object_ref (source);
674         
675         buffer->mode = mode;
676         
677         switch (buffer->mode) {
678         case GMIME_STREAM_BUFFER_BLOCK_READ:
679         case GMIME_STREAM_BUFFER_BLOCK_WRITE:
680                 buffer->buffer = g_malloc (BLOCK_BUFFER_LEN);
681                 buffer->bufend = buffer->buffer + BLOCK_BUFFER_LEN;
682                 buffer->bufptr = buffer->buffer;
683                 buffer->buflen = 0;
684                 break;
685         default:
686                 buffer->buffer = g_malloc (BUFFER_GROW_SIZE);
687                 buffer->bufptr = buffer->buffer;
688                 buffer->bufend = buffer->buffer;
689                 buffer->buflen = BUFFER_GROW_SIZE;
690                 break;
691         }
692         
693         g_mime_stream_construct (GMIME_STREAM (buffer),
694                                  source->bound_start,
695                                  source->bound_end);
696         
697         return GMIME_STREAM (buffer);
698 }
699
700
701 /**
702  * g_mime_stream_buffer_gets:
703  * @stream: stream
704  * @buf: line buffer
705  * @max: max length of a line
706  *
707  * Reads in at most one less than @max characters from @stream and
708  * stores them into the buffer pointed to by @buf. Reading stops after
709  * an EOS or newline ('\n'). If a newline is read, it is stored into
710  * the buffer. A '\0' is stored after the last character in the
711  * buffer.
712  *
713  * Returns: the number of characters read into @buf on success or %-1
714  * on fail.
715  **/
716 ssize_t
717 g_mime_stream_buffer_gets (GMimeStream *stream, char *buf, size_t max)
718 {
719         register char *inptr, *outptr;
720         char *inend, *outend;
721         ssize_t nread, n;
722         char c = '\0';
723         
724         g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
725         
726         outptr = buf;
727         outend = buf + max - 1;
728         
729         if (GMIME_IS_STREAM_BUFFER (stream)) {
730                 GMimeStreamBuffer *buffer = GMIME_STREAM_BUFFER (stream);
731                 
732                 switch (buffer->mode) {
733                 case GMIME_STREAM_BUFFER_BLOCK_READ:
734                         while (outptr < outend) {
735                                 inptr = buffer->bufptr;
736                                 inend = inptr + buffer->buflen;
737                                 
738                                 while (outptr < outend && inptr < inend && *inptr != '\n')
739                                         c = *outptr++ = *inptr++;
740                                 
741                                 if (outptr < outend && inptr < inend && c != '\n')
742                                         c = *outptr++ = *inptr++;
743                                 
744                                 buffer->buflen = inend - inptr;
745                                 buffer->bufptr = inptr;
746                                 
747                                 if (c == '\n')
748                                         break;
749                                 
750                                 if (buffer->buflen == 0) {
751                                         /* buffer more data */
752                                         buffer->bufptr = buffer->buffer;
753                                         n = g_mime_stream_read (buffer->source, buffer->buffer, BLOCK_BUFFER_LEN);
754                                         if (n <= 0)
755                                                 break;
756                                         
757                                         buffer->buflen = n;
758                                 }
759                         }
760                         break;
761                 case GMIME_STREAM_BUFFER_CACHE_READ:
762                         while (outptr < outend) {
763                                 inptr = buffer->bufptr;
764                                 inend = buffer->bufend;
765                                 while (outptr < outend && inptr < inend && *inptr != '\n')
766                                         c = *outptr++ = *inptr++;
767                                 
768                                 if (outptr < outend && inptr < inend && c != '\n')
769                                         c = *outptr++ = *inptr++;
770                                 
771                                 buffer->bufptr = inptr;
772                                 
773                                 if (c == '\n')
774                                         break;
775                                 
776                                 if (inptr == inend && outptr < outend) {
777                                         /* buffer more data */
778                                         size_t offset = (size_t) (buffer->bufptr - buffer->buffer);
779                                         
780                                         buffer->buflen = buffer->bufend - buffer->buffer +
781                                                 MAX (BUFFER_GROW_SIZE, outend - outptr + 1);
782                                         buffer->buffer = g_realloc (buffer->buffer, buffer->buflen);
783                                         buffer->bufend = buffer->buffer + buffer->buflen;
784                                         buffer->bufptr = buffer->buffer + offset;
785                                         nread = g_mime_stream_read (buffer->source, buffer->bufptr,
786                                                                     buffer->bufend - buffer->bufptr);
787                                         
788                                         buffer->bufend = nread >= 0 ? buffer->bufptr + nread : buffer->bufptr;
789                                         
790                                         if (nread <= 0)
791                                                 break;
792                                 }
793                         }
794                         break;
795                 default:
796                         goto slow_and_painful;
797                         break;
798                 }
799                 
800                 /* increment our stream position pointer */
801                 stream->position += (outptr - buf);
802         } else {
803                 /* ugh...do it the slow and painful way... */
804         slow_and_painful:
805                 while (outptr < outend && c != '\n' && (nread = g_mime_stream_read (stream, &c, 1)) == 1)
806                         *outptr++ = c;
807         }
808         
809         if (outptr <= outend) {
810                 /* this should always be true unless @max == 0 */
811                 *outptr = '\0';
812         }
813         
814         return (ssize_t) (outptr - buf);
815 }
816
817
818 /**
819  * g_mime_stream_buffer_readln:
820  * @stream: stream
821  * @buffer: output buffer
822  *
823  * Reads a single line into @buffer.
824  **/
825 void
826 g_mime_stream_buffer_readln (GMimeStream *stream, GByteArray *buffer)
827 {
828         char linebuf[1024];
829         ssize_t len;
830         
831         g_return_if_fail (GMIME_IS_STREAM (stream));
832         
833         while (!g_mime_stream_eos (stream)) {
834                 if ((len = g_mime_stream_buffer_gets (stream, linebuf, sizeof (linebuf))) <= 0)
835                         break;
836                 
837                 if (buffer)
838                         g_byte_array_append (buffer, (unsigned char *) linebuf, len);
839                 
840                 if (linebuf[len - 1] == '\n')
841                         break;
842         }
843 }