src/line_proto.c
changeset 8 be88e543c8ff
child 10 9fe218576d13
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/line_proto.c	Sun Feb 22 08:21:22 2009 +0200
@@ -0,0 +1,131 @@
+
+#include "line_proto.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+/*
+ * Our state
+ */
+struct line_proto {
+    /* The sock_stream we read/write with */
+    struct sock_stream *sock;
+
+    /* Offset of trailing data in buf */
+    size_t tail_offset;
+
+    /* Length of trailing data in buf, if any */
+    size_t tail_len;
+
+    /* Last error */
+    struct error_info err;
+};
+
+err_t line_proto_create (struct line_proto **lp_ptr, struct sock_stream *sock, struct error_info *err)
+{
+    struct line_proto *lp;
+
+    // allocate
+    if ((lp = calloc(1, sizeof(*lp))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
+
+    // store
+    lp->sock = sock;
+
+    // return
+    *lp_ptr = lp;
+
+    return SUCCESS;
+}
+
+/*
+ * This looks for a full '\r\n' terminated line at the beginning of the given buffer. If found, the \r\n will be
+ * replaced with a '\0', and the offset to the beginning of the next line returned. If not found, zero is returned
+ * (which is never a valid next-line offset).
+ *
+ * The given \a hint is an hint as to the offset at which to start scanning, used for incremental invocations of this
+ * on the same buffer.
+ *
+ */
+int _parse_line (char *buf, size_t len, size_t *hint) {
+    int i;
+
+    // empty buffer -> nothing
+    if (len == 0)
+        return 0;
+
+    // look for terminating '\r\n' sequence
+    for (i = *hint; i < len - 1; i++) {
+        // match this + next char
+        if (buf[i] == '\r' && buf[i + 1] == '\n')
+            break;
+    }
+
+    // incomplete?
+    if (i >= len - 1) {
+        *hint = len - 1;
+        return 0;
+    }
+
+    // mangle the newline off
+    buf[i] = '\0';
+
+    // return offset to next line
+    return i + 2;
+}
+
+err_t line_proto_read (struct line_proto *lp, char *buf, size_t len)
+{
+    // offset to recv() new data into, offset to _parse_line hint, offset to next line from _parse_line
+    size_t recv_offset = 0, peek_offset = 0, next_offset = 0;
+    int ret;
+
+    // adjust offset from previous data
+    recv_offset = lp->tail_len;
+
+    // move trailing data from previous line to front of buffer
+    if (lp->tail_offset) {
+        // move to front
+        memmove(buf, buf + lp->tail_offset, lp->tail_len);
+
+        // reset
+        lp->tail_offset = 0;
+        lp->tail_len = 0;
+    }
+
+    // readline loop
+    do {
+        // parse any line at the beginning of the buffer
+        if ((next_offset = _parse_line(buf, recv_offset, &peek_offset)) > 0)
+            break;
+
+        // ensure there's enough space for it
+        assert(recv_offset < len);
+        
+        // otherwise, read more data
+        if ((ret = sock_stream_read(lp->sock, buf + recv_offset, len - recv_offset)) < 0)
+            RETURN_SET_ERROR_INFO(&lp->err, sock_stream_error(lp->sock));
+
+        // EOF?
+        if (ret == 0)
+            return SET_ERROR(&lp->err, ERR_READ_EOF);
+        
+        // update recv_offset
+        recv_offset += ret;
+
+    } while (1);
+
+    // update state for next call
+    lp->tail_offset = next_offset;
+    lp->tail_len = recv_offset - next_offset;
+
+    // ok
+    return SUCCESS;
+}
+
+const struct error_info* line_proto_error (struct line_proto *lp)
+{
+    // return pointer
+    return &lp->err;
+}