# HG changeset patch # User Tero Marttila # Date 1241482525 -10800 # Node ID b54f393c3df0f4bde639ed4841ad81b8e5c2e11e # Parent 1b2f28e26eef6cdcfd15dfcfcf1808c431b834b0 evil chain.h macro magic, fix irc_conn_set_nickname bug, misc. test bugs (mem leaks, missing #includes) diff -r 1b2f28e26eef -r b54f393c3df0 TODO --- 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: diff -r 1b2f28e26eef -r b54f393c3df0 src/chain.c --- 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 +#include -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); - } -} diff -r 1b2f28e26eef -r b54f393c3df0 src/chain.h --- 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 - -/** - * 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 +#include /** - * 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 diff -r 1b2f28e26eef -r b54f393c3df0 src/irc_chan.c --- 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) diff -r 1b2f28e26eef -r b54f393c3df0 src/irc_chan.h --- 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; diff -r 1b2f28e26eef -r b54f393c3df0 src/irc_cmd.c --- 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 #include -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); } + diff -r 1b2f28e26eef -r b54f393c3df0 src/irc_cmd.h --- 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 */ diff -r 1b2f28e26eef -r b54f393c3df0 src/irc_conn.c --- 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); diff -r 1b2f28e26eef -r b54f393c3df0 src/irc_conn.h --- 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; }; /** diff -r 1b2f28e26eef -r b54f393c3df0 src/test/irc_queue.c --- 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); } diff -r 1b2f28e26eef -r b54f393c3df0 src/test/str.c --- 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) { diff -r 1b2f28e26eef -r b54f393c3df0 src/test/transport.c --- 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); }