aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)
{