#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 "6667"
#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))
// XXX: ...
errx(1, "sock_init");
memset(&recvline_ctx, 0, sizeof(recvline_ctx));
// over-simplified connect
if (sock_gnutls_connect(&sock, CONNECT_HOST, CONNECT_SERV, &err))
// XXX:
errx(1, "sock_gnutls_connect");
// 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;
}