istream.c revision 51e1a1c280ccb461a15827f7987d09cb9708b6e3
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
345648b341f228bd7f0b89f8aa3ecb9c470d817eTimo Sirainen#include "ioloop.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "str.h"
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen#include "istream-internal.h"
5a2cb3d097a2d9a9e930af997e7bf3400a8d840dTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenvoid i_stream_destroy(struct istream **stream)
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen i_stream_close(*stream);
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen i_stream_unref(stream);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen}
16f46efe0e090fe6975acf012a61a160f4787985Andrey Panin
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenvoid i_stream_ref(struct istream *stream)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen io_stream_ref(&stream->real_stream->iostream);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen}
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenvoid i_stream_unref(struct istream **stream)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen struct istream_private *_stream = (*stream)->real_stream;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (_stream->iostream.refcount == 1) {
16f46efe0e090fe6975acf012a61a160f4787985Andrey Panin if (_stream->line_str != NULL)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen str_free(&_stream->line_str);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen }
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen io_stream_unref(&(*stream)->real_stream->iostream);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen *stream = NULL;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen}
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#undef i_stream_set_destroy_callback
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenvoid i_stream_set_destroy_callback(struct istream *stream,
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen istream_callback_t *callback, void *context)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen struct iostream_private *iostream = &stream->real_stream->iostream;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen iostream->destroy_callback = callback;
7242e1ce7803b83bc82e239ef111b47c1c72dd4bAndrey Panin iostream->destroy_context = context;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen}
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainenint i_stream_get_fd(struct istream *stream)
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen{
c57776c06ec99ba9b0dafdbf9475ea72ea8ca134Timo Sirainen struct istream_private *_stream = stream->real_stream;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen return _stream->fd;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen}
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainenvoid i_stream_close(struct istream *stream)
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen{
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen io_stream_close(&stream->real_stream->iostream);
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen stream->closed = TRUE;
4051fa1f367553cac34f74c2e332a678390bcee5Timo Sirainen
5965eaa2d972e6264cecaf54091cd43019bc7d1fTimo Sirainen if (stream->stream_errno == 0)
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen stream->stream_errno = ENOENT;
4051fa1f367553cac34f74c2e332a678390bcee5Timo Sirainen}
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainenvoid i_stream_set_max_buffer_size(struct istream *stream, size_t max_size)
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen{
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen io_stream_set_max_buffer_size(&stream->real_stream->iostream, max_size);
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen}
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen
648d24583c1574441c4fa0331a90bd4d6e7996c5Timo Sirainenvoid i_stream_set_return_partial_line(struct istream *stream, bool set)
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen{
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen stream->real_stream->return_nolf_line = set;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen}
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainenssize_t i_stream_read(struct istream *stream)
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen{
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen struct istream_private *_stream = stream->real_stream;
59beb411159176b39e48a52d60dd3239732e67b4Timo Sirainen ssize_t ret;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen if (unlikely(stream->closed))
15cb9549422ccee416b21d26fec97a556ad0fa36Florian Zeitz return -1;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen stream->eof = FALSE;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen stream->stream_errno = 0;
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen ret = _stream->read(_stream);
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen switch (ret) {
704fbadd78375da18dcaf2c5d93ac8cfe2c61358Timo Sirainen case -2:
704fbadd78375da18dcaf2c5d93ac8cfe2c61358Timo Sirainen i_assert(_stream->skip != _stream->pos);
704fbadd78375da18dcaf2c5d93ac8cfe2c61358Timo Sirainen break;
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen case -1:
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen if (stream->stream_errno != 0) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen /* error handling should be easier if we now just
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen assume the stream is now at EOF */
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen stream->eof = TRUE;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen } else {
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen i_assert(stream->eof);
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen }
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen break;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen case 0:
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen i_assert(!stream->blocking);
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen break;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen }
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return ret;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen}
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainenvoid i_stream_skip(struct istream *stream, uoff_t count)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen{
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen struct istream_private *_stream = stream->real_stream;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen size_t data_size;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen data_size = _stream->pos - _stream->skip;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen if (count <= data_size) {
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen /* within buffer */
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen stream->v_offset += count;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen _stream->skip += count;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen }
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen /* have to seek forward */
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen count -= data_size;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen _stream->skip = _stream->pos;
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen stream->v_offset += data_size;
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen if (unlikely(stream->closed))
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen return;
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen stream->stream_errno = 0;
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen _stream->seek(_stream, stream->v_offset + count, FALSE);
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen}
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainenstatic bool i_stream_can_optimize_seek(struct istream *stream)
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen{
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen uoff_t expected_offset;
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen if (stream->real_stream->parent == NULL)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return TRUE;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen /* use the fast route only if the parent stream is at the
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen expected offset */
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen expected_offset = stream->real_stream->parent_start_offset +
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen stream->v_offset - stream->real_stream->skip;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen if (stream->real_stream->parent->v_offset != expected_offset)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return FALSE;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return i_stream_can_optimize_seek(stream->real_stream->parent);
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen}
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainenvoid i_stream_seek(struct istream *stream, uoff_t v_offset)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen{
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen struct istream_private *_stream = stream->real_stream;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen if (v_offset >= stream->v_offset &&
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen i_stream_can_optimize_seek(stream)) {
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen i_stream_skip(stream, v_offset - stream->v_offset);
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen }
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen if (unlikely(stream->closed))
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return;
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen stream->eof = FALSE;
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen _stream->seek(_stream, v_offset, FALSE);
0cc875d28852d15005cff2a77252e7836c862a01Timo Sirainen}
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainenvoid i_stream_seek_mark(struct istream *stream, uoff_t v_offset)
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen{
966cb0c1aa58578339cea6f79b4a423a851ab074Timo Sirainen struct istream_private *_stream = stream->real_stream;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen if (unlikely(stream->closed))
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen stream->eof = FALSE;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen _stream->seek(_stream, v_offset, TRUE);
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen}
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainenvoid i_stream_sync(struct istream *stream)
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen{
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen struct istream_private *_stream = stream->real_stream;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen if (unlikely(stream->closed))
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen return;
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen
21c317a20c4c3784b54fb3e90ee3751870afdcc3Timo Sirainen if (_stream->sync != NULL)
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch _stream->sync(_stream);
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch}
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Boschconst struct stat *i_stream_stat(struct istream *stream, bool exact)
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch{
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch struct istream_private *_stream = stream->real_stream;
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch if (unlikely(stream->closed))
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch return NULL;
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch return _stream->stat(_stream, exact);
4ac2e38bdb450d13b62be41638b12df9e0658009Stephan Bosch}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenbool i_stream_have_bytes_left(const struct istream *stream)
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen{
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen const struct istream_private *_stream = stream->real_stream;
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen return !stream->eof || _stream->skip != _stream->pos;
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen}
59beb411159176b39e48a52d60dd3239732e67b4Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic char *i_stream_next_line_finish(struct istream_private *stream, size_t i)
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen{
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen char *ret;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen size_t end;
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen if (i > 0 && stream->buffer[i-1] == '\r')
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen end = i - 1;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen else
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen end = i;
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainen
15cb9549422ccee416b21d26fec97a556ad0fa36Florian Zeitz if (stream->w_buffer != NULL) {
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainen /* modify the buffer directly */
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen stream->w_buffer[end] = '\0';
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen ret = (char *)stream->w_buffer + stream->skip;
f97cf1c086715db87094bc3d0a4fefdd80bd869cTimo Sirainen } else {
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen /* use a temporary string to return it */
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen if (stream->line_str == NULL)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen stream->line_str = str_new(default_pool, 256);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen str_truncate(stream->line_str, 0);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen str_append_n(stream->line_str, stream->buffer + stream->skip,
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen end - stream->skip);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen ret = str_c_modifiable(stream->line_str);
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen }
8eea67470c1bd8562a62e7445d930bb2079b1a43Timo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (i < stream->pos)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen i++;
59beb411159176b39e48a52d60dd3239732e67b4Timo Sirainen stream->istream.v_offset += i - stream->skip;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen stream->skip = i;
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen return ret;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen}
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainenstatic char *i_stream_last_line(struct istream_private *_stream)
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen{
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen if (_stream->istream.eof && _stream->skip != _stream->pos &&
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen _stream->return_nolf_line) {
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen /* the last line is missing LF and we want to return it. */
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainen return i_stream_next_line_finish(_stream, _stream->pos);
81419a8dd69d8cef0e93d1e04bda77d135202452Timo Sirainen }
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainen return NULL;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen}
b7c2065b3f10f9ae27787a9db5aaefbfc70d4502Timo Sirainen
f97cf1c086715db87094bc3d0a4fefdd80bd869cTimo Sirainenchar *i_stream_next_line(struct istream *stream)
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen{
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen struct istream_private *_stream = stream->real_stream;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen char *ret_buf;
size_t i;
if (_stream->skip >= _stream->pos) {
stream->stream_errno = 0;
return NULL;
}
if (unlikely(_stream->w_buffer == NULL)) {
i_error("i_stream_next_line() called for unmodifiable stream");
return NULL;
}
/* @UNSAFE */
ret_buf = NULL;
for (i = _stream->skip; i < _stream->pos; i++) {
if (_stream->buffer[i] == 10) {
/* got it */
ret_buf = i_stream_next_line_finish(_stream, i);
break;
}
}
if (ret_buf == NULL)
return i_stream_last_line(_stream);
return ret_buf;
}
char *i_stream_read_next_line(struct istream *stream)
{
char *line;
for (;;) {
line = i_stream_next_line(stream);
if (line != NULL)
break;
if (i_stream_read(stream) <= 0)
return i_stream_last_line(stream->real_stream);
}
return line;
}
const unsigned char *
i_stream_get_data(const struct istream *stream, size_t *size_r)
{
const struct istream_private *_stream = stream->real_stream;
if (_stream->skip >= _stream->pos) {
*size_r = 0;
return NULL;
}
*size_r = _stream->pos - _stream->skip;
return _stream->buffer + _stream->skip;
}
unsigned char *i_stream_get_modifiable_data(const struct istream *stream,
size_t *size_r)
{
const struct istream_private *_stream = stream->real_stream;
if (_stream->skip >= _stream->pos || _stream->w_buffer == NULL) {
*size_r = 0;
return NULL;
}
*size_r = _stream->pos - _stream->skip;
return _stream->w_buffer + _stream->skip;
}
int i_stream_read_data(struct istream *stream, const unsigned char **data_r,
size_t *size_r, size_t threshold)
{
ssize_t ret = 0;
bool read_more = FALSE;
do {
*data_r = i_stream_get_data(stream, size_r);
if (*size_r > threshold)
return 1;
/* we need more data */
ret = i_stream_read(stream);
if (ret > 0)
read_more = TRUE;
} while (ret > 0);
*data_r = i_stream_get_data(stream, size_r);
if (ret == -2)
return -2;
if (ret == 0) {
/* need to read more */
i_assert(!stream->blocking);
return 0;
}
if (stream->eof) {
if (read_more) {
/* we read at least some new data */
return 0;
}
} else {
i_assert(stream->stream_errno != 0);
}
return -1;
}
void i_stream_compress(struct istream_private *stream)
{
memmove(stream->w_buffer, stream->w_buffer + stream->skip,
stream->pos - stream->skip);
stream->pos -= stream->skip;
stream->skip = 0;
}
void i_stream_grow_buffer(struct istream_private *stream, size_t bytes)
{
size_t old_size;
old_size = stream->buffer_size;
stream->buffer_size = stream->pos + bytes;
if (stream->buffer_size <= I_STREAM_MIN_SIZE)
stream->buffer_size = I_STREAM_MIN_SIZE;
else
stream->buffer_size = nearest_power(stream->buffer_size);
if (stream->max_buffer_size > 0 &&
stream->buffer_size > stream->max_buffer_size)
stream->buffer_size = stream->max_buffer_size;
stream->buffer = stream->w_buffer =
i_realloc(stream->w_buffer, old_size, stream->buffer_size);
}
bool i_stream_get_buffer_space(struct istream_private *stream,
size_t wanted_size, size_t *size_r)
{
i_assert(wanted_size > 0);
if (wanted_size > stream->buffer_size - stream->pos) {
if (stream->skip > 0) {
/* remove the unused bytes from beginning of buffer */
i_stream_compress(stream);
} else if (stream->max_buffer_size == 0 ||
stream->buffer_size < stream->max_buffer_size) {
/* buffer is full - grow it */
i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE);
}
}
if (size_r != NULL)
*size_r = stream->buffer_size - stream->pos;
return stream->pos != stream->buffer_size;
}
bool i_stream_add_data(struct istream *_stream, const unsigned char *data,
size_t size)
{
struct istream_private *stream = _stream->real_stream;
size_t size2;
(void)i_stream_get_buffer_space(stream, size, &size2);
if (size > size2)
return FALSE;
memcpy(stream->w_buffer + stream->pos, data, size);
stream->pos += size;
return TRUE;
}
static void
i_stream_default_set_max_buffer_size(struct iostream_private *stream,
size_t max_size)
{
struct istream_private *_stream = (struct istream_private *)stream;
_stream->max_buffer_size = max_size;
}
static const struct stat *
i_stream_default_stat(struct istream_private *stream, bool exact ATTR_UNUSED)
{
return &stream->statbuf;
}
struct istream *
i_stream_create(struct istream_private *_stream, struct istream *parent, int fd)
{
_stream->fd = fd;
if (parent != NULL) {
_stream->parent = parent;
_stream->parent_start_offset = parent->v_offset;
_stream->abs_start_offset = parent->v_offset +
parent->real_stream->abs_start_offset;
}
_stream->istream.real_stream = _stream;
if (_stream->stat == NULL)
_stream->stat = i_stream_default_stat;
if (_stream->iostream.set_max_buffer_size == NULL) {
_stream->iostream.set_max_buffer_size =
i_stream_default_set_max_buffer_size;
}
memset(&_stream->statbuf, 0, sizeof(_stream->statbuf));
_stream->statbuf.st_size = -1;
_stream->statbuf.st_atime =
_stream->statbuf.st_mtime =
_stream->statbuf.st_ctime = ioloop_time;
io_stream_init(&_stream->iostream);
return &_stream->istream;
}
#ifdef STREAM_TEST
/* gcc istream.c -o teststream liblib.a -Wall -DHAVE_CONFIG_H -DSTREAM_TEST -g */
#include <fcntl.h>
#include <unistd.h>
#include "ostream.h"
#define BUF_VALUE(offset) \
(((offset) % 256) ^ ((offset) / 256))
static void check_buffer(const unsigned char *data, size_t size, size_t offset)
{
size_t i;
for (i = 0; i < size; i++)
i_assert(data[i] == BUF_VALUE(i+offset));
}
int main(void)
{
struct istream *input, *l_input;
struct ostream *output1, *output2;
int i, fd1, fd2;
unsigned char buf[1024];
const unsigned char *data;
size_t size;
lib_init();
fd1 = open("teststream.1", O_RDWR | O_CREAT | O_TRUNC, 0600);
if (fd1 < 0)
i_fatal("open() failed: %m");
fd2 = open("teststream.2", O_RDWR | O_CREAT | O_TRUNC, 0600);
if (fd2 < 0)
i_fatal("open() failed: %m");
/* write initial data */
for (i = 0; i < sizeof(buf); i++)
buf[i] = BUF_VALUE(i);
write(fd1, buf, sizeof(buf));
/* test reading */
input = i_stream_create_fd(fd1, 512, FALSE);
i_assert(i_stream_get_size(input) == sizeof(buf));
i_assert(i_stream_read_data(input, &data, &size, 0) > 0);
i_assert(size == 512);
check_buffer(data, size, 0);
i_stream_seek(input, 256);
i_assert(i_stream_read_data(input, &data, &size, 0) > 0);
i_assert(size == 512);
check_buffer(data, size, 256);
i_stream_seek(input, 0);
i_assert(i_stream_read_data(input, &data, &size, 512) == -2);
i_assert(size == 512);
check_buffer(data, size, 0);
i_stream_skip(input, 900);
i_assert(i_stream_read_data(input, &data, &size, 0) > 0);
i_assert(size == sizeof(buf) - 900);
check_buffer(data, size, 900);
/* test moving data */
output1 = o_stream_create_fd(fd1, 512, FALSE);
output2 = o_stream_create_fd(fd2, 512, FALSE);
i_stream_seek(input, 1); size = sizeof(buf)-1;
i_assert(o_stream_send_istream(output2, input) == size);
o_stream_flush(output2);
lseek(fd2, 0, SEEK_SET);
i_assert(read(fd2, buf, sizeof(buf)) == size);
check_buffer(buf, size, 1);
i_stream_seek(input, 0);
o_stream_seek(output1, sizeof(buf));
i_assert(o_stream_send_istream(output1, input) == sizeof(buf));
/* test moving with limits */
l_input = i_stream_create_limit(input, sizeof(buf)/2, 512);
i_stream_seek(l_input, 0);
o_stream_seek(output1, 10);
i_assert(o_stream_send_istream(output1, l_input) == 512);
i_stream_set_max_buffer_size(input, sizeof(buf));
i_stream_seek(input, 0);
i_assert(i_stream_read_data(input, &data, &size, sizeof(buf)-1) > 0);
i_assert(size == sizeof(buf));
check_buffer(data, 10, 0);
check_buffer(data + 10, 512, sizeof(buf)/2);
check_buffer(data + 10 + 512,
size - (10 + 512), 10 + 512);
/* reading within limits */
i_stream_seek(l_input, 0);
i_assert(i_stream_read_data(l_input, &data, &size, 511) > 0);
i_assert(size == 512);
i_assert(i_stream_read_data(l_input, &data, &size, 512) == -2);
i_assert(size == 512);
i_stream_skip(l_input, 511);
i_assert(i_stream_read_data(l_input, &data, &size, 0) > 0);
i_assert(size == 1);
i_stream_skip(l_input, 1);
i_assert(i_stream_read_data(l_input, &data, &size, 0) == -1);
i_assert(size == 0);
unlink("teststream.1");
unlink("teststream.2");
return 0;
}
#endif