@@ -22,20 +22,25 @@ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include "stream.h" + +#define BUFFER_SIZE 4096 static bool ctor(void *ptr, va_list args) { CFWStream *stream = ptr; stream->ops = NULL; + stream->cache = NULL; + stream->cache_len = 0; return true; } static void @@ -51,14 +56,182 @@ ssize_t ret; if (stream == NULL || stream->ops == NULL) return -1; - if ((ret = stream->ops->read(stream, buf, len)) < -1) - ret = -1; + if (stream->cache == NULL) { + if ((ret = stream->ops->read(stream, buf, len)) < -1) + ret = -1; + + return ret; + } + + if (len >= stream->cache_len) { + ret = stream->cache_len; + + memcpy(buf, stream->cache, stream->cache_len); + + free(stream->cache); + stream->cache = NULL; + stream->cache_len = 0; + + return ret; + } else { + char *tmp; + + if ((tmp = malloc(stream->cache_len - len)) == NULL) + return -1; + memcpy(tmp, stream->cache + len, stream->cache_len - len); + memcpy(buf, stream->cache, len); + + free(stream->cache); + stream->cache = tmp; + stream->cache_len -= len; + + return len; + } +} + +CFWString* +cfw_stream_read_line(void *ptr) +{ + CFWStream *stream = ptr; + CFWString *ret; + char *buf, *ret_str, *new_cache; + ssize_t buf_len; + size_t i, ret_len; + + /* Look if there is a line or \0 in our cache */ + if (stream->cache != NULL) { + for (i = 0; i < stream->cache_len; i++) { + if (stream->cache[i] == '\n' || + stream->cache[i] == '\0') { + ret_len = i; + if (i > 0 && stream->cache[i - 1] == '\r') + ret_len--; + + ret_str = cfw_strndup(stream->cache, ret_len); + if (ret_str == NULL) + return NULL; + + ret = cfw_create(cfw_string, NULL); + if (ret == NULL) { + free(ret_str); + return NULL; + } + cfw_string_set_nocopy(ret, ret_str, ret_len); + + new_cache = malloc(stream->cache_len - i - 1); + if (new_cache == NULL) + return NULL; + memcpy(new_cache, stream->cache + i + 1, + stream->cache_len - i - 1); + + free(stream->cache); + stream->cache = new_cache; + stream->cache_len -= i + 1; + + return ret; + } + } + } + + /* Read and see if we get a newline or \0 */ + + if ((buf = malloc(BUFFER_SIZE)) == NULL) + return NULL; + + for (;;) { + if (stream->ops->eof(stream)) { + free(buf); + + if (stream->cache == NULL) + return NULL; + + ret_len = stream->cache_len; + + if (ret_len > 0 && stream->cache[ret_len - 1] == '\r') + ret_len--; + + ret_str = cfw_strndup(stream->cache, ret_len); + if (ret_str == NULL) + return NULL; + + ret = cfw_create(cfw_string, NULL); + if (ret == NULL) { + free(ret_str); + return NULL; + } + cfw_string_set_nocopy(ret, ret_str, ret_len); + + free(stream->cache); + stream->cache = NULL; + stream->cache_len = 0; + + return ret; + } + + buf_len = stream->ops->read(stream, buf, BUFFER_SIZE); + if (buf_len == -1) { + free(buf); + return NULL; + } + + /* Look if there's a newline or \0 */ + for (i = 0; i < buf_len; i++) { + if (buf[i] == '\n' || buf[i] == '\0') { + ret_len = stream->cache_len + i; + + if ((ret_str = malloc(ret_len + 1)) == NULL) { + /* + * FIXME: We lost the current buffer. + * Mark the stream as broken? + */ + free(buf); + return NULL; + } + memcpy(ret_str, stream->cache, + stream->cache_len); + memcpy(ret_str + stream->cache_len, buf, i); + if (ret_len > 0 && ret_str[ret_len - 1] == '\r') + ret_len--; + ret_str[ret_len] = '\0'; + + ret = cfw_create(cfw_string, NULL); + if (ret == NULL) { + free(buf); + free(ret_str); + return NULL; + } + cfw_string_set_nocopy(ret, ret_str, ret_len); + + new_cache = malloc(buf_len - i - 1); + if (new_cache == NULL) { + free(buf); + return NULL; + } + memcpy(new_cache, buf + i + 1, buf_len - i - 1); + + free(stream->cache); + stream->cache = new_cache; + stream->cache_len = buf_len - i - 1; + + free(buf); + return ret; + } + } - return ret; + /* There was no newline or \0 */ + new_cache = realloc(stream->cache, stream->cache_len + buf_len); + if (new_cache == NULL) { + free(buf); + return NULL; + } + memcpy(new_cache + stream->cache_len, buf, buf_len); + stream->cache = new_cache; + stream->cache_len += buf_len; + } } bool cfw_stream_write(void *ptr, const void *buf, size_t len) { @@ -81,10 +254,13 @@ { CFWStream *stream = ptr; if (stream == NULL || stream->ops == NULL) return true; + + if (stream->cache != NULL) + return false; return stream->ops->eof(stream); } void