evil chain.h macro magic, fix irc_conn_set_nickname bug, misc. test bugs (mem leaks, missing #includes)
authorTero Marttila <terom@fixme.fi>
Tue, 05 May 2009 03:15:25 +0300
changeset 171 b54f393c3df0
parent 170 1b2f28e26eef
child 172 ea4972e51fa3
evil chain.h macro magic, fix irc_conn_set_nickname bug, misc. test bugs (mem leaks, missing #includes)
TODO
src/chain.c
src/chain.h
src/irc_chan.c
src/irc_chan.h
src/irc_cmd.c
src/irc_cmd.h
src/irc_conn.c
src/irc_conn.h
src/test/irc_queue.c
src/test/str.c
src/test/transport.c
--- a/TODO	Mon May 04 23:19:50 2009 +0300
+++ b/TODO	Tue May 05 03:15:25 2009 +0300
@@ -1,5 +1,5 @@
 sock:
- * sock_openssl (as sock_gnutls is kind of 'meh' somehow)
+ * sock_openssl?
  * tests...
 
 irc_queue:
@@ -10,7 +10,7 @@
  * handle irc_cmd_remove from inside irc_cmd_invoke
 
 irc_net:
- * reconnect, maybe cycling servers?
+ * maybe cycling servers on reconnect?
  * proper case-insensitive lookups for channel names
 
 config:
--- a/src/chain.c	Mon May 04 23:19:50 2009 +0300
+++ b/src/chain.c	Tue May 05 03:15:25 2009 +0300
@@ -1,61 +1,26 @@
 #include "chain.h"
 
 #include <stdlib.h>
+#include <assert.h>
 
-err_t chain_add (struct chain_list *list, const void *chain, void *arg)
+void *_chain_add (struct _chain_head *head, bool tail, size_t size)
 {
-    struct chain_head *item;
+    struct _chain_item *item;
+
+    // sanity-check
+    assert(size >= sizeof(item));
 
     // allocate the chain item
-    if ((item = calloc(1, sizeof(*item))) == NULL)
-        return ERR_CALLOC;
-
-    // store
-    item->chain = chain;
-    item->arg = arg;
-
-    // append
-    STAILQ_INSERT_TAIL(list, item, node);
+    if ((item = calloc(1, size)) == NULL)
+        return NULL;
+    
+    // insert
+    if (tail)
+        TAILQ_INSERT_TAIL(head, item, _chain_list);
+    else
+        TAILQ_INSERT_HEAD(head, item, _chain_list);
 
     // ok
-    return SUCCESS;
+    return item;
 }
 
-void chain_remove (struct chain_list *list, const void *chain, void *arg)
-{
-    struct chain_head *item;
-    
-    // look for it
-    CHAIN_FOREACH(list, item) {
-        if (item->chain == chain && item->arg == arg) {
-            // remove it
-            // XXX: use TAILQ instead?
-            STAILQ_REMOVE(list, item, chain_head, node);
-
-            // free it
-            free(item);
-
-            // ok
-            return;
-        }
-    }
-
-    // not found... ignore
-}
-
-void chain_free (struct chain_list *list)
-{
-    // start from the first item
-    struct chain_head *next = STAILQ_FIRST(list);
-
-    // clean up any handler chains
-    while (next) {
-        struct chain_head *node = next;
-
-        // update next
-        next = STAILQ_NEXT(node, node);
-
-        // free
-        free(node);
-    }
-}
--- a/src/chain.h	Mon May 04 23:19:50 2009 +0300
+++ b/src/chain.h	Tue May 05 03:15:25 2009 +0300
@@ -3,71 +3,136 @@
 
 /**
  * @file
- * 
- * Defines a semi-generalized "chain of things" behaviour.
  *
- * The structure of this is a chain_list, which contains a number of chain_head items, which then contain a number of
- * induvidual items.
+ * Defines a brain-dead auto-allocated linked list for simple uses.
  */
 #include "error.h"
 #include <sys/queue.h>
-
-/**
- * The chain header.
- */
-struct chain_head {
-    /** The list of items */
-    const void *chain;
-    
-    /** The context arg */
-    void *arg;
-    
-    /** Our position in the chain_list */
-    STAILQ_ENTRY(chain_head) node;
-};
-
-/**
- * @struct chain_list
- *
- * The chain list
- */
-STAILQ_HEAD(chain_list, chain_head);
-
-/**
- * Initialize a `struct chain_list`
- */
-#define CHAIN_INIT(list_ptr) STAILQ_INIT(list_ptr)
+#include <stdlib.h>
+#include <stdbool.h>
 
 /**
- * Add an item onto the end of a chain_list
+ * Header entry to embed a chain_item
  */
-err_t chain_add (struct chain_list *list, const void *chain, void *arg);
+#define CHAIN_ITEM_HEADER(type) TAILQ_ENTRY(type) _chain_list
 
 /**
- * Iterate over the contents of a chain_list
- *
- * struct chain_list *list = ...;
- * struct chain_head *head;
- * struct foo *foo;
- *
- *  CHAIN_FOREACH(list, head) {
- *      for (foo = head->chain; foo->bar; foo++) {
- *          foo->bar(foo, head->arg);
- *      }
- *  }
+ * Header entry to embed a chain_head
  */
-#define CHAIN_FOREACH(list_ptr, head_ptr) STAILQ_FOREACH(head_ptr, list_ptr, node)
+#define CHAIN_HEAD_TYPE(head_type, item_type) \
+    struct head_type {  \
+        TAILQ_HEAD(head_type ## _head, item_type) head;  \
+    }
 
 /**
- * Remove an item added with chain_add from a chain_list, matching against the given chain/arg.
- *
- * If no item matches, nothing is done.
+ * Generic chain_head type
  */
-void chain_remove (struct chain_list *list, const void *chain, void *arg);
+struct _chain_item {
+   TAILQ_ENTRY(_chain_item) _chain_list;
+};
+
+TAILQ_HEAD(_chain_head, _chain_item);
 
 /**
- * Free a chain_list
+ * Cast to a chain_item
  */
-void chain_free (struct chain_list *list);
+#define CHAIN_CAST_ITEM(item_ptr) &(item_ptr)->_chain_list
+
+/**
+ * Ref the head struct from the given chain_head
+ */
+#define CHAIN_DEREF_HEAD(head_ptr) &(head_ptr)->head
+
+/**
+ * Cast to a generic _chain_head
+ */
+#define CHAIN_HEAD_GENERIC(head_ptr) (struct _chain_head *) CHAIN_DEREF_HEAD(head_ptr)
+
+/**
+ * Evaluates to the type of the given CHAIN_HEAD
+ */
+#define CHAIN_TYPE(head_ptr) typeof (*TAILQ_FIRST(CHAIN_DEREF_HEAD(head_ptr)))
+
+
+
+/**
+ * Initialize a chain_head to be empty
+ */
+#define CHAIN_INIT(head_ptr) TAILQ_INIT(CHAIN_DEREF_HEAD(head_ptr))
+
+
+/**
+ * Return the first item in the chain as a pointer of the correct type
+ */
+#define CHAIN_FIRST(head_ptr) TAILQ_FIRST(CHAIN_DEREF_HEAD(head_ptr))
+
+/**
+ * Return the item after the given item
+ */
+#define CHAIN_NEXT(item_ptr) TAILQ_NEXT(item_ptr, _chain_list)
+
+
+
+/**
+ * Allocate a new chain_item of the given type, adding it to the beginning of the list, and returning a calloc'd
+ * struct of the given type.
+ */
+#define CHAIN_ADD_HEAD(head_ptr) (CHAIN_TYPE(head_ptr) *) _chain_add(CHAIN_HEAD_GENERIC(head_ptr), false, sizeof (*CHAIN_TYPE(head_ptr)))
+
+/**
+ * Same as CHAIN_ADD_HEAD, except this adds the new item to the end of the list.
+ */
+#define CHAIN_ADD_TAIL(head_ptr) (CHAIN_TYPE(head_ptr) *) _chain_add(CHAIN_HEAD_GENERIC(head_ptr), true,  sizeof (CHAIN_TYPE(head_ptr)))
+
+
+
+/**
+ * Iterate over the items in a chain
+ */
+#define CHAIN_FOREACH(head_ptr, item_ptr) TAILQ_FOREACH(item_ptr, CHAIN_DEREF_HEAD(head_ptr), _chain_list)
+
+/**
+ * Safely iterate over the items in a chain, such that the iterated-over item can be removed without breaking the chain
+ */
+#define CHAIN_FOREACH_SAFE(head_ptr, item_ptr) \
+    for (   \
+            typeof (item_ptr) _chain_next = CHAIN_FIRST(head_ptr); \
+            ((void) ((item_ptr = _chain_next) && (_chain_next = CHAIN_NEXT(_chain_next)))), item_ptr; \
+            (void) 0  \
+    )
+
+
+/**
+ * Remove an item from the list and free it.
+ */
+#define CHAIN_DELETE(head_ptr, item_ptr) \
+    do {    \
+        TAILQ_REMOVE(CHAIN_DEREF_HEAD(head_ptr), item_ptr, _chain_list);    \
+        free(item_ptr); \
+    } while (0);
+
+/**
+ * Delete the items from the chain for which the given predicate matches
+ */
+#define CHAIN_DELETE_WHICH(head_ptr, item_ptr, predicate) \
+    do {    \
+        CHAIN_FOREACH_SAFE(head_ptr, item_ptr)  \
+            if (predicate)  \
+                CHAIN_DELETE(head_ptr, item_ptr);   \
+    } while (0)
+
+/**
+ * Delete all items from the chain
+ */
+#define CHAIN_CLEAR(head_ptr) \
+    do {    \
+        CHAIN_TYPE(head_ptr) *_chain_item;   \
+        CHAIN_FOREACH_SAFE(head_ptr, _chain_item)   \
+           free(_chain_item);   \
+    } while (0);
+
+
+
+void *_chain_add (struct _chain_head *head, bool tail, size_t size);
 
 #endif
--- a/src/irc_chan.c	Mon May 04 23:19:50 2009 +0300
+++ b/src/irc_chan.c	Tue May 05 03:15:25 2009 +0300
@@ -16,15 +16,13 @@
  */
 #define IRC_CHAN_INVOKE_CALLBACK(chan, _cb_name_, ...)          \
     do {                                                        \
-        struct chain_head *head;                                \
+        struct irc_chan_callback_item *item;                    \
                                                                 \
-        CHAIN_FOREACH(&(chan)->callbacks, head) {               \
-            const struct irc_chan_callbacks *callbacks = head->chain;       \
-                                                                \
-            if (callbacks->_cb_name_)                           \
-                callbacks->_cb_name_((chan), ## __VA_ARGS__, head->arg);    \
+        CHAIN_FOREACH_SAFE(&(chan)->callbacks, item) {          \
+            if (item->callbacks->_cb_name_)                     \
+                item->callbacks->_cb_name_((chan), ## __VA_ARGS__, item->arg);  \
         }                                                       \
-    } while (0);
+    } while (0)
 
 /**
  * Add or update a nickname to the irc_chan.users list.
@@ -162,6 +160,7 @@
     struct irc_chan *chan = arg;
 
     (void) line;
+    (void) chan;
 
     // XXX: update state
     log_info("channel join sync complete");
@@ -325,20 +324,34 @@
     }
 
     // free chan itself
-    irc_cmd_free(&chan->handlers);
-    chain_free(&chan->callbacks);
+    irc_cmd_clear(&chan->handlers);
+    CHAIN_CLEAR(&chan->callbacks);
     free((char *) chan->info.channel);
     free(chan);
 }
 
 err_t irc_chan_add_callbacks (struct irc_chan *chan, const struct irc_chan_callbacks *callbacks, void *arg)
 {
-    return chain_add(&chan->callbacks, callbacks, arg);
+    struct irc_chan_callback_item *item;
+  
+    // create a new item
+    if ((item = CHAIN_ADD_TAIL(&chan->callbacks)) == NULL)
+        return ERR_MEM;
+
+    // store
+    item->callbacks = callbacks;
+    item->arg = arg;
+
+    // ok
+    return SUCCESS;
 }
 
 void irc_chan_remove_callbacks (struct irc_chan *chan, const struct irc_chan_callbacks *callbacks, void *arg)
 {
-    chain_remove(&chan->callbacks, callbacks, arg);
+    struct irc_chan_callback_item *item;
+    
+    // remove all matching callback_items
+    CHAIN_DELETE_WHICH(&chan->callbacks, item, item->callbacks == callbacks && item->arg == arg);
 }
 
 struct irc_chan_user* irc_chan_get_user (struct irc_chan *chan, const char *nickname)
--- a/src/irc_chan.h	Mon May 04 23:19:50 2009 +0300
+++ b/src/irc_chan.h	Tue May 05 03:15:25 2009 +0300
@@ -81,6 +81,17 @@
 };
 
 /**
+ * Callback storage struct
+ */
+struct irc_chan_callback_item {
+    CHAIN_ITEM_HEADER(irc_chan_callback_item);
+
+    const struct irc_chan_callbacks *callbacks;
+
+    void *arg;
+};
+
+/**
  * Persistent IRC channel state, part of irc_net.
  *
  * This stores the channel's info, status flags, users list, and handlers/callbacks.
@@ -119,10 +130,10 @@
     LIST_HEAD(irc_chan_users_list, irc_chan_user) users;
     
     /** General command handlers */
-    irc_cmd_handlers_t handlers;
+    struct irc_cmd_handlers handlers;
 
     /** High-level user callbacks */
-    struct chain_list callbacks;
+    CHAIN_HEAD_TYPE(irc_chan_callback_list, irc_chan_callback_item) callbacks;
 
     /** The irc_net::channels list */
     TAILQ_ENTRY(irc_chan) net_channels;
--- a/src/irc_cmd.c	Mon May 04 23:19:50 2009 +0300
+++ b/src/irc_cmd.c	Tue May 05 03:15:25 2009 +0300
@@ -3,28 +3,39 @@
 #include <stdlib.h>
 #include <string.h>
 
-void irc_cmd_init (irc_cmd_handlers_t *handlers)
+void irc_cmd_init (struct irc_cmd_handlers *handlers)
 {
     CHAIN_INIT(handlers);
 }
 
-err_t irc_cmd_add (irc_cmd_handlers_t *handlers, const struct irc_cmd_handler *list, void *arg)
+err_t irc_cmd_add (struct irc_cmd_handlers *handlers, const struct irc_cmd_handler list[], void *arg)
 {
-    return chain_add(handlers, list, arg);
+    struct irc_cmd_table *table;
+
+    // alloc/add
+    if ((table = CHAIN_ADD_TAIL(handlers)) == NULL)
+        return ERR_MEM;
+
+    // store
+    table->list = list;
+    table->arg = arg;
+
+    // ok
+    return SUCCESS;
 }
 
-void irc_cmd_invoke (irc_cmd_handlers_t *handlers, const struct irc_line *line)
+void irc_cmd_invoke (struct irc_cmd_handlers *handlers, const struct irc_line *line)
 {
-    struct chain_head *head;
+    struct irc_cmd_table *table;
     const struct irc_cmd_handler *handler;
     
-    CHAIN_FOREACH(handlers, head) {
+    CHAIN_FOREACH_SAFE(handlers, table) {
         // look up appropriate handler
-        for (handler = head->chain; handler->command; handler++) {
+        for (handler = table->list; handler->command; handler++) {
             // the command is alpha-only, so normal case-insensitive cmp is fine
             if (strcasecmp(handler->command, line->command) == 0) {
                 // invoke the func
-                handler->func(line, head->arg);
+                handler->func(line, table->arg);
 
                 // ...only one per chain
                 break;
@@ -33,12 +44,16 @@
     }
 }
 
-void irc_cmd_remove (irc_cmd_handlers_t *handlers, const struct irc_cmd_handler *list, void *arg)
+void irc_cmd_remove (struct irc_cmd_handlers *handlers, const struct irc_cmd_handler list[], void *arg)
 {
-    chain_remove(handlers, list, arg);
+    struct irc_cmd_table *table;
+
+    // delete all matching items
+    CHAIN_DELETE_WHICH(handlers, table, table->list == list && table->arg == arg);
 }
 
-void irc_cmd_free (irc_cmd_handlers_t *handlers)
+void irc_cmd_clear (struct irc_cmd_handlers *handlers)
 {
-    chain_free(handlers);
+    CHAIN_CLEAR(handlers);
 }
+
--- a/src/irc_cmd.h	Mon May 04 23:19:50 2009 +0300
+++ b/src/irc_cmd.h	Tue May 05 03:15:25 2009 +0300
@@ -18,11 +18,11 @@
  * Note that when an irc_line is matched against an array of these, only the *first* matching handler is invoked.
  */
 struct irc_cmd_handler {
-    /** The command name to match */
+    /** Command name to match */
     const char *command;
 
     /**
-     * The handler function.
+     * Handler function.
      *
      * @param line the irc_line that matched the command
      * @param arg the context arg, as given to irc_cmd_add.
@@ -31,46 +31,51 @@
 };
 
 /**
- * A dynamic list of irc_cmd_handler's
+ * A registered list of irc_cmd_handlers
  */
-typedef struct chain_list irc_cmd_handlers_t;
+struct irc_cmd_table {
+    CHAIN_ITEM_HEADER(irc_cmd_table);
+
+    /** NULL-terminated array of handlers */
+    const struct irc_cmd_handler *list;
+
+    /** Context argument*/
+    void *arg;
+};
+
+CHAIN_HEAD_TYPE(irc_cmd_handlers, irc_cmd_table);
 
 /**
- * Initialize a irc_cmd_handlers list.
+ * Initialize a irc_cmd_handlers.
  */
-void irc_cmd_init (irc_cmd_handlers_t *handlers);
+void irc_cmd_init (struct irc_cmd_handlers *handlers);
 
 /**
- * Append the given NULL-termianted array of irc_cmd_handler's to the irc_cmd_handlers list, without copying it.
+ * Append the given NULL-termianted array of irc_cmd_handler's to the irc_cmd_handlers, without copying it.
  *
- * @param handlers the irc_cmd_handlers_t
- * @param list the { NULL, NULL } termianted array of irc_cmd_handlers
- * @param arg the opaque context argument, will be passed to the func's of the given list when invoked
+ * @param list the { NULL, NULL } termianted array of irc_cmd_handler's
+ * @param arg the context argument
  */
-err_t irc_cmd_add (irc_cmd_handlers_t *handlers, const struct irc_cmd_handler *list, void *arg);
+err_t irc_cmd_add (struct irc_cmd_handlers *handlers, const struct irc_cmd_handler list[], void *arg);
 
 /**
  * Trigger all relevant handlers for the given irc_line.
  *
- * @param handlers the irc_cmd_handlers_t
  * @param line the line to match against the handlers and invoke the func with
  */
-void irc_cmd_invoke (irc_cmd_handlers_t *handlers, const struct irc_line *line);
+void irc_cmd_invoke (struct irc_cmd_handlers *handlers, const struct irc_line *line);
 
 /**
- * Remove a previously added handler list.
+ * Remove a previously added handler handlers.
  *
- * @param handlers the irc_cmd_handlers_t
  * @param list the list given to irc_cmd_add, compared as pointer value
  * @param arg the context arg given to irc_cmd_add, compared as pointer value
  */
-void irc_cmd_remove (irc_cmd_handlers_t *handlers, const struct irc_cmd_handler *list, void *arg);
+void irc_cmd_remove (struct irc_cmd_handlers *handlers, const struct irc_cmd_handler list[], void *arg);
 
 /**
- * Cleanup an irc_cmd_handlers list, releasing all memory.
- *
- * @param handlers the irc_cmd_handlers_t to cleanup
+ * Clears an irc_cmd_list, releasing all memory allocated by the.
  */
-void irc_cmd_free (irc_cmd_handlers_t *handlers);
+void irc_cmd_clear (struct irc_cmd_handlers *handlers);
 
 #endif /* IRC_CMD_H */
--- a/src/irc_conn.c	Mon May 04 23:19:50 2009 +0300
+++ b/src/irc_conn.c	Tue May 05 03:15:25 2009 +0300
@@ -29,6 +29,9 @@
 {
     struct error_info err;
 
+    // drop old nickname
+    free(conn->nickname);
+
     // strdup
     if ((conn->nickname = strdup(nickname)) == NULL) {
         SET_ERROR(&err, ERR_STRDUP);
@@ -299,8 +302,8 @@
         irc_queue_destroy(conn->out_queue);
     
     // the command handlers
-    irc_cmd_free(&conn->handlers);
-    irc_cmd_free(&conn->ctcp_handlers);
+    irc_cmd_clear(&conn->handlers);
+    irc_cmd_clear(&conn->ctcp_handlers);
 
     // additional data
     free(conn->nickname);
--- a/src/irc_conn.h	Mon May 04 23:19:50 2009 +0300
+++ b/src/irc_conn.h	Tue May 05 03:15:25 2009 +0300
@@ -135,7 +135,7 @@
      * @see irc_line
      * @see irc_conn::ctcp_handlers
      */
-    irc_cmd_handlers_t handlers;
+    struct irc_cmd_handlers handlers;
 
     /** 
      * CTCP command handlers. These handlers are invoked for all PRIVMSG's recieved from the IRC server which begin
@@ -159,7 +159,7 @@
      * @see irc_line
      * @see irc_conn::handlers
      */
-    irc_cmd_handlers_t ctcp_handlers;
+    struct irc_cmd_handlers ctcp_handlers;
 };
 
 /**
--- a/src/test/irc_queue.c	Mon May 04 23:19:50 2009 +0300
+++ b/src/test/irc_queue.c	Tue May 05 03:15:25 2009 +0300
@@ -72,5 +72,6 @@
 
     // cleanup
     irc_queue_destroy(queue);
+    line_proto_destroy(lp);
 }
 
--- a/src/test/str.c	Mon May 04 23:19:50 2009 +0300
+++ b/src/test/str.c	Tue May 05 03:15:25 2009 +0300
@@ -3,7 +3,9 @@
  *
  * Test functions for the str module.
  */
-#include "assert.h"
+#include "test.h"
+
+#include "../str.h"
 
 void assert_str_quote (size_t buf_size, const char *data, ssize_t len, const char *target, size_t out)
 {
--- a/src/test/transport.c	Mon May 04 23:19:50 2009 +0300
+++ b/src/test/transport.c	Tue May 05 03:15:25 2009 +0300
@@ -109,6 +109,6 @@
     assert_transport_data(tp, "");
 
     // cleanup
-    transport_test_destroy(tp);
+    transport_destroy(transport);
 }