aboutsummaryrefslogtreecommitdiff
path: root/degesch.c
diff options
context:
space:
mode:
authorPřemysl Janouch <p.janouch@gmail.com>2015-04-19 02:12:59 +0200
committerPřemysl Janouch <p.janouch@gmail.com>2015-04-19 02:12:59 +0200
commit5502975505737f93b19a4cbbcc890dee3ac898e9 (patch)
tree96e735cfaa2a8fd8c0fb5fb630de80bf54669e6a /degesch.c
parent8c38b1b9b8907d2380e96172e417b6208f32fe18 (diff)
downloadxK-5502975505737f93b19a4cbbcc890dee3ac898e9.tar.gz
xK-5502975505737f93b19a4cbbcc890dee3ac898e9.tar.xz
xK-5502975505737f93b19a4cbbcc890dee3ac898e9.zip
degesch: prepare for user/channel buffers
That is, prepare all the required data structures and their relations. I'm not particularly good at this, so let's just hope it will work out.
Diffstat (limited to 'degesch.c')
-rw-r--r--degesch.c213
1 files changed, 190 insertions, 23 deletions
diff --git a/degesch.c b/degesch.c
index 79fecfd..364fb69 100644
--- a/degesch.c
+++ b/degesch.c
@@ -91,7 +91,7 @@ static struct config_item g_config_table[] =
// --- Application data --------------------------------------------------------
// All text stored in our data structures is encoded in UTF-8.
-// Or at least should be.
+// Or at least should be. The exception is IRC identifiers.
/// Shorthand to set an error and return failure from the function
#define FAIL(...) \
@@ -100,25 +100,161 @@ static struct config_item g_config_table[] =
return false; \
BLOCK_END
+static void user_unref (void *p);
+static void channel_unref (void *p);
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+struct user_channel
+{
+ LIST_HEADER (struct user_channel)
+
+ struct channel *channel; ///< Reference to channel
+};
+
+static struct user_channel *
+user_channel_new (void)
+{
+ struct user_channel *self = xcalloc (1, sizeof *self);
+ return self;
+}
+
+static void
+user_channel_destroy (void *p)
+{
+ struct user_channel *self = p;
+ channel_unref (self->channel);
+ free (self);
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-struct nick_info
+// We keep references to user information in channels and buffers,
+// as well as in the name lookup table.
+
+struct user
{
+ size_t ref_count; ///< Reference count
+
+ // TODO: eventually a reference to the server
+
char *nickname; ///< Literal nickname
- char mode_char; ///< Op/voice/... character
+ // TODO: write code to poll for the away status
bool away; ///< User is away
- // XXX: maybe a good candidate for deduplication (away status)
+ struct user_channel *channels; ///< Channels the user is on
};
+static struct user *
+user_new (void)
+{
+ struct user *self = xcalloc (1, sizeof *self);
+ self->ref_count = 1;
+ return self;
+}
+
static void
-nick_info_destroy (void *p)
+user_destroy (struct user *self)
{
- struct nick_info *self = p;
free (self->nickname);
+ LIST_FOR_EACH (struct user_channel, iter, self->channels)
+ user_channel_destroy (iter);
+ free (self);
+}
+
+static struct user *
+user_ref (struct user *self)
+{
+ self->ref_count++;
+ return self;
+}
+
+static void
+user_unref (void *p)
+{
+ struct user *self = p;
+ if (!--self->ref_count)
+ user_destroy (self);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+struct channel_user
+{
+ LIST_HEADER (struct channel_user)
+
+ struct user *user; ///< Reference to user
+ char mode_char; ///< Op/voice/... character, or zero
+};
+
+static struct channel_user *
+channel_user_new (void)
+{
+ struct channel_user *self = xcalloc (1, sizeof *self);
+ return self;
+}
+
+static void
+channel_user_destroy (void *p)
+{
+ struct channel_user *self = p;
+ user_unref (self->user);
+ free (self);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+// We keep references to channels in their users and buffers,
+// as well as in the name lookup table.
+
+struct channel
+{
+ size_t ref_count; ///< Reference count
+
+ // TODO: eventually a reference to the server
+
+ char *name; ///< Channel name
+ char *mode; ///< Channel mode
+ char *topic; ///< Channel topic
+
+ struct channel_user *users; ///< Channel users
+};
+
+static struct channel *
+channel_new (void)
+{
+ struct channel *self = xcalloc (1, sizeof *self);
+ self->ref_count = 1;
+ return self;
+}
+
+static void
+channel_destroy (void *p)
+{
+ struct channel *self = p;
+ free (self->name);
+ free (self->mode);
+ free (self->topic);
+ LIST_FOR_EACH (struct channel_user, iter, self->users)
+ channel_user_destroy (iter);
free (self);
}
+static struct channel *
+channel_ref (struct channel *self)
+{
+ self->ref_count++;
+ return self;
+}
+
+static void
+channel_unref (void *p)
+{
+ struct channel *self = p;
+ if (!--self->ref_count)
+ channel_destroy (self);
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
enum buffer_line_flags
@@ -202,20 +338,18 @@ struct buffer
unsigned unseen_messages_count; ///< # messages since last visited
- // Channel information:
+ // Origin information:
- char *mode; ///< Channel mode
- char *topic; ///< Channel topic
- struct str_map nicks; ///< Maps nicks to "nick_info"
+ struct user *user; ///< Reference to user
+ struct channel *channel; ///< Reference to channel
+
+ // TODO: eventually a reference to the server for server buffers
};
static struct buffer *
buffer_new (void)
{
struct buffer *self = xcalloc (1, sizeof *self);
- str_map_init (&self->nicks);
- self->nicks.key_xfrm = irc_strxfrm;
- self->nicks.free = nick_info_destroy;
return self;
}
@@ -225,9 +359,12 @@ buffer_destroy (struct buffer *self)
free (self->name);
// Can't really free "history" here
free (self->saved_line);
- free (self->mode);
- free (self->topic);
- str_map_free (&self->nicks);
+ LIST_FOR_EACH (struct buffer_line, iter, self->lines)
+ buffer_line_destroy (iter);
+ if (self->user)
+ user_unref (self->user);
+ if (self->channel)
+ channel_unref (self->channel);
free (self);
}
@@ -260,7 +397,23 @@ struct app_context
SSL_CTX *ssl_ctx; ///< SSL context
SSL *ssl; ///< SSL connection
- // TODO: channels?
+ // TODO: an output queue to prevent excess floods (this will be needed
+ // especially for away status polling)
+
+ // XXX: there can be buffers for non-existent users
+ // TODO: user buffers rename on nick changes
+ // TODO: move entries in "irc_buffer_map" and "irc_users" when that happens
+ // TODO: user buffers may merge when an existing user renames to match
+ // the name of a buffer for a non-existent user
+ // TODO: initialize key_strxfrm according to server properties;
+ // note that collisions may arise on reconnecting
+ // TODO: when disconnected, get rid of all users everywhere;
+ // maybe also broadcast all buffers about the disconnection event
+ // TODO: when getting connected again, rejoin all current channels
+
+ struct str_map irc_users; ///< IRC user data
+ struct str_map irc_channels; ///< IRC channel data
+ struct str_map irc_buffer_map; ///< Maps IRC identifiers to buffers
// TODO: initialize and update these two values
// TODO: probably issue a USERHOST message for ourselves after connecting
@@ -288,6 +441,7 @@ struct app_context
struct buffer *buffers_tail; ///< The tail of our buffers
// XXX: when we go multiserver, there will be collisions
+ // TODO: make buffer names unique like weechat does
struct str_map buffers_by_name; ///< Excludes GLOBAL and SERVER
struct buffer *global_buffer; ///< The global buffer
@@ -328,13 +482,22 @@ app_context_init (struct app_context *self)
str_init (&self->read_buffer);
self->irc_ready = false;
+ str_map_init (&self->irc_users);
+ self->irc_users.free = user_unref;
+ self->irc_users.key_xfrm = irc_strxfrm;
+ str_map_init (&self->irc_channels);
+ self->irc_channels.free = channel_unref;
+ self->irc_channels.key_xfrm = irc_strxfrm;
+ str_map_init (&self->irc_buffer_map);
+ self->irc_buffer_map.key_xfrm = irc_strxfrm;
+
+ poller_init (&self->poller);
+
str_map_init (&self->buffers_by_name);
self->buffers_by_name.key_xfrm = irc_strxfrm;
self->last_displayed_msg_time = time (NULL);
- poller_init (&self->poller);
-
char *encoding = nl_langinfo (CODESET);
#ifdef __linux__
encoding = xstrdup_printf ("%s//TRANSLIT", encoding);
@@ -370,15 +533,19 @@ app_context_free (struct app_context *self)
if (self->ssl_ctx)
SSL_CTX_free (self->ssl_ctx);
+ str_map_free (&self->irc_users);
+ str_map_free (&self->irc_channels);
+ str_map_free (&self->irc_buffer_map);
+
free (self->irc_nickname);
free (self->irc_user_mode);
+ poller_free (&self->poller);
+
LIST_FOR_EACH (struct buffer, iter, self->buffers)
buffer_destroy (iter);
str_map_free (&self->buffers_by_name);
- poller_free (&self->poller);
-
iconv_close (self->latin1_to_utf8);
iconv_close (self->term_from_utf8);
iconv_close (self->term_to_utf8);
@@ -1373,8 +1540,8 @@ make_prompt (struct app_context *ctx, struct str *output)
str_append_printf (output, "%d:%s",
buffer_get_index (ctx, buffer), buffer->name);
- if (buffer->type == BUFFER_CHANNEL && *buffer->mode)
- str_append_printf (output, "(%s)", buffer->mode);
+ if (buffer->type == BUFFER_CHANNEL && *buffer->channel->mode)
+ str_append_printf (output, "(%s)", buffer->channel->mode);
if (buffer != ctx->global_buffer)
{