view src/lib/ostream-crlf.c @ 4891:6ab2712f1a93 HEAD

Only imap binary was actually working.
author Timo Sirainen <tss@iki.fi>
date Sun, 10 Dec 2006 14:35:02 +0200
parents 928229f8b3e6
children be4fb0976c1d
line wrap: on
line source

/* Copyright (c) 2004 Timo Sirainen */

/* The code is quite ugly because we want the send functions to return correcly
   the number of input bytes consumed, not number of bytes actually sent. */

#include "lib.h"
#include "buffer.h"
#include "istream.h"
#include "ostream-internal.h"
#include "ostream-crlf.h"

#define IOVBUF_COUNT 64

struct crlf_ostream {
	struct _ostream ostream;

        struct ostream *output;
	bool last_cr;
};

static const struct const_iovec cr_iov = { "\r", 1 };

static void _close(struct _iostream *stream __attr_unused__)
{
}

static void _destroy(struct _iostream *stream)
{
	struct crlf_ostream *cstream = (struct crlf_ostream *)stream;

	o_stream_unref(&cstream->output);
}

static void _set_max_buffer_size(struct _iostream *stream, size_t max_size)
{
	struct crlf_ostream *cstream = (struct crlf_ostream *)stream;

	o_stream_set_max_buffer_size(cstream->output, max_size);
}

static void _cork(struct _ostream *stream, bool set)
{
	struct crlf_ostream *cstream = (struct crlf_ostream *)stream;

	if (set)
		o_stream_cork(cstream->output);
	else
		o_stream_uncork(cstream->output);
}

static int _flush(struct _ostream *stream)
{
	struct crlf_ostream *cstream = (struct crlf_ostream *)stream;

	return o_stream_flush(cstream->output);
}

static void _flush_pending(struct _ostream *stream, bool set)
{
	struct crlf_ostream *cstream = (struct crlf_ostream *)stream;

	o_stream_set_flush_pending(cstream->output, set);
}

static size_t _get_used_size(struct _ostream *stream)
{
	struct crlf_ostream *cstream = (struct crlf_ostream *)stream;

	return o_stream_get_buffer_used_size(cstream->output);
}

static int _seek(struct _ostream *stream, uoff_t offset)
{
	struct crlf_ostream *cstream = (struct crlf_ostream *)stream;
	int ret;

	cstream->last_cr = FALSE;
	ret = o_stream_seek(cstream->output, offset);
	stream->ostream.offset = cstream->output->offset;
	return ret;
}

static ssize_t
sendv_crlf(struct crlf_ostream *cstream, const struct const_iovec *iov,
	   size_t iov_count, const char *diff, ssize_t *total_r)
{
	ssize_t ret;
	size_t i, pos;

	ret = o_stream_sendv(cstream->output, iov, iov_count);
	if (ret > 0) {
		pos = (size_t)ret - 1;
		for (i = 0; i < iov_count && pos >= iov[i].iov_len; i++) {
			*total_r += iov[i].iov_len + diff[i];
			pos -= iov[i].iov_len;
		}

		cstream->last_cr =
			*((const char *)iov[i].iov_base + pos) == '\r';

		if (pos + 1 == iov[i].iov_len)
			*total_r += iov[i].iov_len + diff[i];
		else
			*total_r += pos;
	}
	cstream->ostream.ostream.offset = cstream->output->offset;
	return ret;
}

static ssize_t
_sendv_crlf(struct _ostream *stream, const struct const_iovec *iov,
	    unsigned int iov_count)
{
	struct crlf_ostream *cstream = (struct crlf_ostream *)stream;
	buffer_t *iov_buf, *diff_buf;
	const unsigned char *data;
	struct const_iovec new_iov;
	unsigned int vec, i, len, start, new_iov_count = 0, new_iov_size = 0;
	ssize_t ret, total;
	bool last_cr;

	last_cr = cstream->last_cr;

	t_push();
	iov_buf = buffer_create_dynamic(unsafe_data_stack_pool,
					sizeof(struct const_iovec *) *
					IOVBUF_COUNT);
	diff_buf = buffer_create_dynamic(unsafe_data_stack_pool, IOVBUF_COUNT);
	total = 0;
	for (vec = 0; vec < iov_count; vec++) {
		data = iov[vec].iov_base;
		len = iov[vec].iov_len;

		for (i = start = 0;; i++) {
			if (i != len) {
				if (data[i] != '\n')
					continue;

				if (i > 0) {
					if (data[i-1] == '\r')
						continue;
				} else {
					if (last_cr)
						continue;
				}

				/* need to insert CR */
			}

			if (i != start) {
				new_iov.iov_base = data + start;
				new_iov.iov_len = i - start;

				buffer_append(iov_buf, &new_iov,
					      sizeof(new_iov));
				buffer_append_c(diff_buf, 0);
				new_iov_count++;
				new_iov_size += new_iov.iov_len;
			}
			start = i;

			if (i != len) {
				buffer_append(iov_buf, &cr_iov, sizeof(cr_iov));
				buffer_append_c(diff_buf, -1);
				new_iov_count++;
				new_iov_size++;
			}

			if (new_iov_count >= IOVBUF_COUNT-1) {
				ret = sendv_crlf(cstream, iov_buf->data,
						 new_iov_count, diff_buf->data,
						 &total);
				if (ret != (ssize_t)new_iov_size) {
					t_pop();
					return ret < 0 ? ret : total;
				}

				buffer_set_used_size(iov_buf, 0);
				buffer_set_used_size(diff_buf, 0);
				new_iov_count = 0;
				new_iov_size = 0;
			}

			if (i == len)
				break;
		}

		if (len != 0)
			last_cr = data[len-1] == '\r';
	}

	ret = sendv_crlf(cstream, iov_buf->data, new_iov_count,
			 diff_buf->data, &total);
	t_pop();
	return ret < 0 ? ret : total;
}

static ssize_t
sendv_lf(struct crlf_ostream *cstream, const struct const_iovec *iov,
	 size_t iov_count, const char *diff, ssize_t *total_r)
{
	ssize_t ret;
	size_t i, left;

	ret = o_stream_sendv(cstream->output, iov, iov_count);
	if (ret >= 0) {
		left = (size_t)ret;
		for (i = 0; i < iov_count && left >= iov[i].iov_len; i++) {
			*total_r += iov[i].iov_len + diff[i];
			left -= iov[i].iov_len;
		}
		*total_r += left;
	}
	cstream->ostream.ostream.offset = cstream->output->offset;
	return ret;
}

static ssize_t
_sendv_lf(struct _ostream *stream, const struct const_iovec *iov,
	  unsigned int iov_count)
{
	struct crlf_ostream *cstream = (struct crlf_ostream *)stream;
	buffer_t *iov_buf, *diff_buf;
	const unsigned char *data;
	struct const_iovec new_iov;
	unsigned int vec, i, len, start, next;
	unsigned int new_iov_count = 0, new_iov_size = 0;
	ssize_t ret, total;
	int diff;

	t_push();
	iov_buf = buffer_create_dynamic(unsafe_data_stack_pool,
					sizeof(struct const_iovec *) *
					IOVBUF_COUNT);
	diff_buf = buffer_create_dynamic(unsafe_data_stack_pool, IOVBUF_COUNT);
	total = 0;
	for (vec = 0; vec < iov_count; vec++) {
		data = iov[vec].iov_base;
		len = iov[vec].iov_len;

		for (i = start = 0;; i++) {
			if (i != len) {
				if (data[i] != '\n' || i == 0 ||
				    data[i-1] != '\r')
					continue;
			}

			if (start == 0 && i > 0 && data[0] != '\n' &&
			    cstream->last_cr) {
				/* bare CR, keep it */
				buffer_append(iov_buf, &cr_iov, sizeof(cr_iov));
				buffer_append_c(diff_buf, -1);
				new_iov_count++;
				new_iov_size++;
			}

			next = i;
			if (i != len) {
				/* skipping an CR */
				i--;
				cstream->last_cr = FALSE;
				diff = 1;
			} else if (i != start && data[i-1] == '\r') {
				/* data ends with CR, don't add it yet */
				i--;
				cstream->last_cr = TRUE;
				diff = 1;
			} else {
				/* data doesn't end with CR */
				cstream->last_cr = FALSE;
				diff = 0;
			}

			new_iov.iov_base = data + start;
			new_iov.iov_len = i - start;

			buffer_append(iov_buf, &new_iov, sizeof(new_iov));
			buffer_append_c(diff_buf, diff);
			new_iov_count++;
			new_iov_size += new_iov.iov_len;

			start = i = next;

			if (new_iov_count >= IOVBUF_COUNT-1) {
				ret = sendv_lf(cstream, iov_buf->data,
					       new_iov_count, diff_buf->data,
					       &total);
				stream->ostream.offset =
					cstream->output->offset;
				if (ret != (ssize_t)new_iov_size) {
					t_pop();
					return ret < 0 ? ret : total;
				}

				buffer_set_used_size(iov_buf, 0);
				buffer_set_used_size(diff_buf, 0);
				new_iov_count = 0;
				new_iov_size = 0;
			}

			if (i == len)
				break;
		}
	}

	if (new_iov_count == 0) {
		/* Tried to send only CR. */
		ret = 0;
	} else {
		ret = sendv_lf(cstream, iov_buf->data, new_iov_count,
			       diff_buf->data, &total);
	}
	stream->ostream.offset = cstream->output->offset;

	t_pop();
	return ret < 0 ? ret : total;
}

static off_t
_send_istream(struct _ostream *outstream, struct istream *instream)
{
	struct const_iovec iov;
        const unsigned char *data;
	size_t sent = 0;
	ssize_t ret;

	while ((ret = i_stream_read_data(instream, &data,
					 &iov.iov_len, 0)) != -1) {
		if (iov.iov_len == 0)
			return sent;

		iov.iov_base = data;
		ret = o_stream_sendv(&outstream->ostream, &iov, 1);
		if (ret <= 0)
			return ret < 0 && sent == 0 ? -1 : (ssize_t)sent;

		i_assert((size_t)ret <= iov.iov_len);
		i_stream_skip(instream, ret);
		sent += ret;

		if ((size_t)ret != iov.iov_len)
			return sent;
	}

	return sent == 0 && instream->stream_errno != 0 ? -1 : (ssize_t)sent;
}

static struct crlf_ostream *
o_stream_create_common(pool_t pool, struct ostream *output)
{
	struct crlf_ostream *cstream;

	cstream = p_new(pool, struct crlf_ostream, 1);
	cstream->output = output;
	o_stream_ref(output);

	cstream->ostream.iostream.close = _close;
	cstream->ostream.iostream.destroy = _destroy;
	cstream->ostream.iostream.set_max_buffer_size = _set_max_buffer_size;

	cstream->ostream.cork = _cork;
	cstream->ostream.flush = _flush;
	cstream->ostream.flush_pending = _flush_pending;
	cstream->ostream.get_used_size = _get_used_size;
	cstream->ostream.seek = _seek;
	cstream->ostream.send_istream = _send_istream;
	return cstream;
}

struct ostream *o_stream_create_crlf(pool_t pool, struct ostream *output)
{
	struct crlf_ostream *cstream;

	cstream = o_stream_create_common(pool, output);
	cstream->ostream.sendv = _sendv_crlf;
	return _o_stream_create(&cstream->ostream, pool);
}

struct ostream *o_stream_create_lf(pool_t pool, struct ostream *output)
{
	struct crlf_ostream *cstream;

	cstream = o_stream_create_common(pool, output);
	cstream->ostream.sendv = _sendv_lf;
	return _o_stream_create(&cstream->ostream, pool);
}