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.
This commit is contained in:
Přemysl Eric Janouch 2015-04-19 02:12:59 +02:00
parent 8c38b1b9b8
commit 5502975505
1 changed files with 194 additions and 27 deletions

221
degesch.c
View File

@ -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,27 +100,163 @@ static struct config_item g_config_table[] =
return false; \
BLOCK_END
static void user_unref (void *p);
static void channel_unref (void *p);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct nick_info
struct user_channel
{
char *nickname; ///< Literal nickname
char mode_char; ///< Op/voice/... character
bool away; ///< User is away
LIST_HEADER (struct user_channel)
// XXX: maybe a good candidate for deduplication (away status)
struct channel *channel; ///< Reference to channel
};
static void
nick_info_destroy (void *p)
static struct user_channel *
user_channel_new (void)
{
struct nick_info *self = p;
free (self->nickname);
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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 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
// TODO: write code to poll for the away status
bool away; ///< User is away
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
user_destroy (struct user *self)
{
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
{
BUFFER_LINE_HIGHLIGHT = 1 << 0 ///< The user was highlighted by this
@ -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)
{