Index: src/file.c ================================================================== --- src/file.c +++ src/file.c @@ -142,12 +142,12 @@ CFWFile *file = ptr; const char *path = va_arg(args, const char*); const char *mode = va_arg(args, const char*); int flags; - /* Make sure we have a valid pointer in case we error out */ - file->stream.ops = NULL; + /* Make sure we have a valid file in case we error out */ + cfw_stream->ctor(ptr, args); file->eof = false; if ((flags = parse_mode(mode)) == -1) return false; @@ -160,11 +160,11 @@ } static void dtor(void *ptr) { - cfw_stream_close(ptr); + cfw_stream->dtor(ptr); } static CFWClass class = { .name = "CFWFile", .size = sizeof(CFWFile), Index: src/stream.c ================================================================== --- src/stream.c +++ src/stream.c @@ -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 Index: src/stream.h ================================================================== --- src/stream.h +++ src/stream.h @@ -29,10 +29,11 @@ #include #include "class.h" #include "object.h" +#include "string.h" struct cfw_stream_ops { ssize_t (*read)(void*, void*, size_t); bool (*write)(void*, const void*, size_t); bool (*eof)(void*); @@ -40,14 +41,17 @@ }; typedef struct CFWStream { CFWObject obj; struct cfw_stream_ops *ops; + char *cache; + size_t cache_len; } CFWStream; extern CFWClass *cfw_stream; extern ssize_t cfw_stream_read(void*, void*, size_t); +extern CFWString* cfw_stream_read_line(void*); extern bool cfw_stream_write(void*, const void*, size_t); extern bool cfw_stream_write_string(void*, const char*); extern bool cfw_stream_eof(void*); extern void cfw_stream_close(void*); #endif Index: src/tcpsocket.c ================================================================== --- src/tcpsocket.c +++ src/tcpsocket.c @@ -94,10 +94,12 @@ static bool ctor(void *ptr, va_list args) { CFWTCPSocket *sock = ptr; + cfw_stream->ctor(ptr, args); + sock->fd = -1; sock->stream.ops = &stream_ops; sock->eof = false; return true; @@ -104,11 +106,11 @@ } static void dtor(void *ptr) { - cfw_stream_close(ptr); + cfw_stream->dtor(ptr); } bool cfw_tcpsocket_connect(CFWTCPSocket *sock, const char *host, uint16_t port) {