initial code...
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile Sun Feb 22 02:00:34 2009 +0200
@@ -0,0 +1,81 @@
+#LIBEVENT_PATH = ~/opt
+#LIBRARY_PATHS = -L${LIBEVENT_PATH}/lib
+#INCLUDE_PATHS = -I${LIBEVENT_PATH}/include
+LDLIBS = -levent
+
+# default is test
+ifndef MODE
+MODE = test
+endif
+
+ifeq ($(MODE), debug)
+MODE_CFLAGS = -g -DDEBUG_ENABLED
+else ifeq ($(MODE), dev)
+MODE_CFLAGS = -g
+else ifeq ($(MODE), test)
+MODE_CFLAGS = -g -DINFO_DISABLED
+else ifeq ($(MODE), release)
+MODE_CFLAGS = -DINFO_DISABLED -O2
+endif
+
+FIXED_CFLAGS = -Wall -std=gnu99
+
+BIN_NAMES = nexus
+BIN_PATHS = $(addprefix bin/,$(BIN_NAMES))
+
+# modules
+module_objs = $(patsubst src/%.c,obj/%.o,$(wildcard src/$(1)/*.c))
+
+# XXX: not yet there
+#CORE_OBJS = obj/lib/log.o obj/lib/signals.o
+
+# first target
+all: ${BIN_PATHS}
+
+# binaries
+bin/nexus:
+
+# computed
+LDFLAGS = ${LIBRARY_PATHS}
+CPPFLAGS = ${INCLUDE_PATHS} ${DEFINES}
+CFLAGS = ${MODE_CFLAGS} ${FIXED_CFLAGS}
+
+SRC_PATHS = $(wildcard src/*.c) $(wildcard src/*/*.c)
+SRC_NAMES = $(patsubst src/%,%,$(SRC_PATHS))
+SRC_DIRS = $(dir $(SRC_NAMES))
+
+# other targets
+clean :
+ -rm obj/*.o obj/*/*.o
+ -rm bin/*
+ -rm build/deps/*.d build/deps/*/*.d
+
+clean-deps:
+ -rm build/deps/*/*.d
+ -rm build/deps/*.d
+
+#obj-dirs:
+# python build/make_obj_dirs.py $(BIN_PATHS)
+
+build/deps/%.d : src/%.c
+ @set -e; rm -f $@; \
+ $(CC) -MM -MT __ $(CPPFLAGS) $< > $@.$$$$; \
+ sed 's,__[ :]*,obj/$*.o $@ : ,g' < $@.$$$$ > $@; \
+ rm -f $@.$$$$
+
+include $(SRC_NAMES:%.c=build/deps/%.d)
+
+obj/%.o : src/%.c
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
+
+bin/% : obj/%.o
+ $(CC) $(LDFLAGS) $+ $(LOADLIBES) $(LDLIBS) -o $@
+
+# documentation
+DOXYGEN_PATH = /usr/bin/doxygen
+DOXYGEN_CONF_PATH = doc/doxygen.conf
+DOXYGEN_OUTPUT_FILE = doc/html/index.html
+
+docs :
+ ${DOXYGEN_PATH} ${DOXYGEN_CONF_PATH}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/nexus.c Sun Feb 22 02:00:34 2009 +0200
@@ -0,0 +1,168 @@
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <event.h>
+#include <assert.h>
+
+#include <err.h>
+
+#define CONNECT_HOST "irc.fixme.fi"
+#define CONNECT_SERV "6667"
+#define LINE_LENGTH 512
+
+/*
+ * Simple getaddrinfo() connect
+ */
+int sock_connect (const char *host, const char *service) {
+ struct addrinfo hints, *res, *r;
+ int _err, sock = -1;
+
+ // hints
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ // resolve
+ if ((_err = getaddrinfo(host, service, &hints, &res)))
+ errx(1, "getaddrinfo: %s", gai_strerror(_err));
+
+ // use
+ for (r = res; r; r = r->ai_next) {
+ // XXX: wrong
+ if ((sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) < 0)
+ err(1, "socket");
+
+ if (connect(sock, r->ai_addr, r->ai_addrlen))
+ err(1, "connect");
+
+ break;
+ }
+
+ // ensure we got some valid socket
+ if (sock < 0)
+ errx(1, "no valid socket");
+
+ // ok
+ return sock;
+}
+
+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 (int 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 = 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) {
+ int sock;
+ char line_buf[LINE_LENGTH + 1];
+ struct recvline_state recvline_ctx;
+
+ // initialize
+ memset(&recvline_ctx, 0, sizeof(recvline_ctx));
+
+ // over-simplified connect
+ sock = sock_connect(CONNECT_HOST, CONNECT_SERV);
+
+ // 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;
+}
+