split off line_proto, and make sock_stream_error return a const error_info
authorTero Marttila <terom@fixme.fi>
Sun, 22 Feb 2009 08:21:22 +0200
changeset 8 be88e543c8ff
parent 7 844f014409ff
child 9 4c4c906cc649
split off line_proto, and make sock_stream_error return a const error_info
Makefile
src/error.c
src/error.h
src/line_proto.c
src/line_proto.h
src/nexus.c
src/sock.c
src/sock.h
src/sock_tcp.c
--- a/Makefile	Sun Feb 22 07:53:34 2009 +0200
+++ b/Makefile	Sun Feb 22 08:21:22 2009 +0200
@@ -33,6 +33,7 @@
 CORE_OBJS = obj/error.o
 SOCK_OBJS = obj/sock.o obj/sock_tcp.o
 SOCK_GNUTLS_OBJS = obj/sock_gnutls.o
+LINEPROTO_OBJS = obj/line_proto.o
 
 # XXX: not yet there
 #CORE_OBJS = obj/lib/log.o obj/lib/signals.o
@@ -41,7 +42,7 @@
 all: ${BIN_PATHS}
 
 # binaries
-bin/nexus: ${CORE_OBJS} ${SOCK_OBJS} ${SOCK_GNUTLS_OBJS}
+bin/nexus: ${CORE_OBJS} ${SOCK_OBJS} ${SOCK_GNUTLS_OBJS} ${LINEPROTO_OBJS}
 
 # computed
 CFLAGS = ${MODE_CFLAGS} ${FIXED_CFLAGS} ${LIBEVENT_CFLAGS} ${GNUTLS_CFLAGS}
--- a/src/error.c	Sun Feb 22 07:53:34 2009 +0200
+++ b/src/error.c	Sun Feb 22 08:21:22 2009 +0200
@@ -17,10 +17,11 @@
     switch (code) {
         ERROR_NAME( ERR_CALLOC,                         "calloc"                                        );
         ERROR_NAME( ERR_GETADDRINFO,                    "getaddrinfo"                                   );
-        ERROR_NAME( ERR_GETADDRINFO_EMPTY,              "getaddrinfo"                                   );
+        ERROR_NAME( ERR_GETADDRINFO_EMPTY,              "getaddrinfo: no results"                       );
         ERROR_NAME( ERR_SOCKET,                         "socket"                                        );
         ERROR_NAME( ERR_CONNECT,                        "connect"                                       );
         ERROR_NAME( ERR_READ,                           "read"                                          );
+        ERROR_NAME( ERR_READ_EOF,                       "read: EOF"                                     );
         ERROR_NAME( ERR_WRITE,                          "write"                                         );
         ERROR_NAME( ERR_GNUTLS_CERT_ALLOC_CRED,         "gnutls_certificate_allocate_credentials"       );
         ERROR_NAME( ERR_GNUTLS_GLOBAL_INIT,             "gnutls_global_init"                            );
@@ -32,7 +33,7 @@
     }
 }
 
-const char *error_msg (struct error_info *err)
+const char *error_msg (const struct error_info *err)
 {
     static char msg[ERROR_MSG_MAXLEN];
 
--- a/src/error.h	Sun Feb 22 07:53:34 2009 +0200
+++ b/src/error.h	Sun Feb 22 08:21:22 2009 +0200
@@ -48,7 +48,8 @@
 
     /* Low-level IO errors */
     _ERROR_CODE( ERR_READ,                          0x000401,   ERRNO   ),
-    _ERROR_CODE( ERR_WRITE,                         0x000402,   ERRNO   ),
+    _ERROR_CODE( ERR_READ_EOF,                      0x000402,   NONE    ),
+    _ERROR_CODE( ERR_WRITE,                         0x000403,   ERRNO   ),
 
     /* GnuTLS errors */
     _ERROR_CODE( ERR_GNUTLS_CERT_ALLOC_CRED,        0x010101,   GNUTLS  ),
@@ -88,7 +89,7 @@
  *
  * This is returned as a pointer into a statically allocated buffer. It is not re-entrant.
  */
-const char *error_msg (struct error_info *err);
+const char *error_msg (const struct error_info *err);
 
 /** No error, evaulates as logical false */
 #define SUCCESS (0)
@@ -123,6 +124,7 @@
 #define RETURN_SET_ERROR(err_info_ptr, err_code) do { SET_ERROR(err_info_ptr, err_code); return (err_code); } while (0)
 #define RETURN_SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra); return (err_code); } while (0)
 #define RETURN_SET_ERROR_ERRNO(err_info_ptr, err_code) do { _SET_ERROR_ERRNO(err_info_ptr, err_code); return (err_code); } while (0)
+#define RETURN_SET_ERROR_INFO(err_info_ptr, from_ptr) do { SET_ERROR_INFO(err_info_ptr, from_ptr); return (from_ptr->code); } while (0)
 
 /* Same as above, but also do a 'goto error' */
 #define JUMP_SET_ERROR(err_info_ptr, err_code) do { SET_ERROR(err_info_ptr, err_code); goto error; } while (0)
--- /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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/line_proto.h	Sun Feb 22 08:21:22 2009 +0200
@@ -0,0 +1,33 @@
+#ifndef LINE_PROTO_H
+#define LINE_PROTO_H
+
+/*
+ * Support for protocols that send/receive lines
+ */
+#include "sock.h"
+#include "error.h"
+
+/*
+ * The state handle
+ */
+struct line_proto;
+
+/*
+ * Create a new line_proto off the the given sock_stream. The newly allocated line_proto will be returned via *lp_ptr.
+ */
+err_t line_proto_create (struct line_proto **lp_ptr, struct sock_stream *sock, struct error_info *err);
+
+/*
+ * Receive one line into the given buffer. The line will be terminated with '\r\n', and said terminator will be
+ * NUL'd out, so the buffer is safe for use as a C-string after succesfull return.
+ *
+ * Note: currently this uses the buffer to store intermediate state, so always pass the same buffer (for now).
+ */
+err_t line_proto_read (struct line_proto *lp, char *buf, size_t len);
+
+/*
+ * Get current error_info*
+ */
+const struct error_info* line_proto_error (struct line_proto *lp);
+
+#endif /* LINE_PROTO_H */
--- a/src/nexus.c	Sun Feb 22 07:53:34 2009 +0200
+++ b/src/nexus.c	Sun Feb 22 08:21:22 2009 +0200
@@ -9,121 +9,35 @@
 #include <err.h>
 
 #include "sock.h"
+#include "line_proto.h"
 
 #define CONNECT_HOST "irc.fixme.fi"
-#define CONNECT_SERV "66976"
+#define CONNECT_SERV "6697"
 #define LINE_LENGTH 512
 
-struct recvline_state {
-    size_t tail_offset;
-    size_t tail_len;
-};
-
-/*
- * 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;
-}
-
-/*
- * Receive one line of data into the buffer of the given length
- */
-int recvline (struct sock_stream *sock, char *buf, size_t len, struct recvline_state *ctx) {
-    size_t recv_offset = 0, peek_offset = 0, next_offset = 0;
-    int ret;
-
-    // adjust offset from previous data
-    recv_offset = ctx->tail_len;
-
-    // move trailing data from previous line to front of buffer
-    if (ctx->tail_offset) {
-        // move to front
-        memmove(buf, buf + ctx->tail_offset, ctx->tail_len);
-
-        // reset
-        ctx->tail_offset = 0;
-        ctx->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(sock, buf + recv_offset, len - recv_offset)) < 0)
-            err(1, "read");
-
-        // EOF?
-        if (ret == 0)
-            errx(1, "read: EOF");
-        
-        // update recv_offset
-        recv_offset += ret;
-    } while (1);
-
-    // update state for next call
-    ctx->tail_offset = next_offset;
-    ctx->tail_len = recv_offset - next_offset;
-
-    // ok
-    return 0;
-}
-
 int main (int argc, char **argv) {
     struct sock_stream *sock;
+    struct line_proto *lp;
     char line_buf[LINE_LENGTH + 1];
-    struct recvline_state recvline_ctx;
     struct error_info err;
 
     // initialize
     if (sock_init(&err))
         errx(1, "sock_init: %s", error_msg(&err));
 
-    memset(&recvline_ctx, 0, sizeof(recvline_ctx));
-    
     // over-simplified connect
     if (sock_gnutls_connect(&sock, CONNECT_HOST, CONNECT_SERV, &err))
         errx(1, "sock_gnutls_connect: %s", error_msg(&err));
 
+    // line protocol
+    if (line_proto_create(&lp, sock, &err))
+        errx(1, "line_proto_create: %s", error_msg(&err));
+
     // read lines and dump them out
     do {
         // recv
-        recvline(sock, line_buf, sizeof(line_buf), &recvline_ctx);
+        if (line_proto_read(lp, line_buf, sizeof(line_buf)))
+            errx(1, "line_proto_read: %s", error_msg(line_proto_error(lp)));
 
         // printf
         printf("<<< %s\n", line_buf);
--- a/src/sock.c	Sun Feb 22 07:53:34 2009 +0200
+++ b/src/sock.c	Sun Feb 22 08:21:22 2009 +0200
@@ -35,8 +35,8 @@
     return sock->type->methods.write(sock, buf, len);
 }
 
-void sock_stream_error (struct sock_stream *sock, struct error_info *err)
+const struct error_info* sock_stream_error (struct sock_stream *sock)
 {
-    // copy from SOCK_ER
-    *err = *SOCK_ERR(sock);
+    // return pointer
+    return SOCK_ERR(sock);
 }
--- a/src/sock.h	Sun Feb 22 07:53:34 2009 +0200
+++ b/src/sock.h	Sun Feb 22 08:21:22 2009 +0200
@@ -43,8 +43,8 @@
 err_t sock_stream_write (struct sock_stream *sock, const void *buf, size_t len);
 
 /**
- * Get last err_info for \a sock, returned via \a *err.
+ * Get current error_info for \a sock.
  */
-void sock_stream_error (struct sock_stream *sock, struct error_info *err);
+const struct error_info* sock_stream_error (struct sock_stream *sock);
 
 #endif
--- a/src/sock_tcp.c	Sun Feb 22 07:53:34 2009 +0200
+++ b/src/sock_tcp.c	Sun Feb 22 08:21:22 2009 +0200
@@ -148,8 +148,8 @@
 
     // connect
     if ((err = sock_tcp_init_connect(sock, host, service))) {
-        // set *err_info
-        sock_stream_error(SOCK_TCP_BASE(sock), err_info);
+        // copy error_info
+        SET_ERROR_INFO(err_info, sock_stream_error(SOCK_TCP_BASE(sock)));
 
         // cleanup
         sock_tcp_release(sock);