src/nexus.c
author Tero Marttila <terom@fixme.fi>
Sun, 22 Feb 2009 07:53:34 +0200
changeset 7 844f014409ff
parent 6 240ae8482d64
child 8 be88e543c8ff
permissions -rw-r--r--
and then error_msg

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <event.h>
#include <assert.h>

#include <err.h>

#include "sock.h"

#define CONNECT_HOST "irc.fixme.fi"
#define CONNECT_SERV "66976"
#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;
    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));

    // read lines and dump them out
    do {
        // recv
        recvline(sock, line_buf, sizeof(line_buf), &recvline_ctx);

        // printf
        printf("<<< %s\n", line_buf);

    } while (1);
    
    // ok
    return 0;
}