evil chain.h macro magic, fix irc_conn_set_nickname bug, misc. test bugs (mem leaks, missing #includes)
--- 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);
}