--- /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;
+}