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:
parent
8c38b1b9b8
commit
5502975505
221
degesch.c
221
degesch.c
|
@ -91,7 +91,7 @@ static struct config_item g_config_table[] =
|
||||||
// --- Application data --------------------------------------------------------
|
// --- Application data --------------------------------------------------------
|
||||||
|
|
||||||
// All text stored in our data structures is encoded in UTF-8.
|
// 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
|
/// Shorthand to set an error and return failure from the function
|
||||||
#define FAIL(...) \
|
#define FAIL(...) \
|
||||||
|
@ -100,27 +100,163 @@ static struct config_item g_config_table[] =
|
||||||
return false; \
|
return false; \
|
||||||
BLOCK_END
|
BLOCK_END
|
||||||
|
|
||||||
|
static void user_unref (void *p);
|
||||||
|
static void channel_unref (void *p);
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
struct nick_info
|
struct user_channel
|
||||||
{
|
{
|
||||||
char *nickname; ///< Literal nickname
|
LIST_HEADER (struct user_channel)
|
||||||
char mode_char; ///< Op/voice/... character
|
|
||||||
bool away; ///< User is away
|
|
||||||
|
|
||||||
// XXX: maybe a good candidate for deduplication (away status)
|
struct channel *channel; ///< Reference to channel
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static struct user_channel *
|
||||||
nick_info_destroy (void *p)
|
user_channel_new (void)
|
||||||
{
|
{
|
||||||
struct nick_info *self = p;
|
struct user_channel *self = xcalloc (1, sizeof *self);
|
||||||
free (self->nickname);
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
user_channel_destroy (void *p)
|
||||||
|
{
|
||||||
|
struct user_channel *self = p;
|
||||||
|
channel_unref (self->channel);
|
||||||
free (self);
|
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
|
enum buffer_line_flags
|
||||||
{
|
{
|
||||||
BUFFER_LINE_HIGHLIGHT = 1 << 0 ///< The user was highlighted by this
|
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
|
unsigned unseen_messages_count; ///< # messages since last visited
|
||||||
|
|
||||||
// Channel information:
|
// Origin information:
|
||||||
|
|
||||||
char *mode; ///< Channel mode
|
struct user *user; ///< Reference to user
|
||||||
char *topic; ///< Channel topic
|
struct channel *channel; ///< Reference to channel
|
||||||
struct str_map nicks; ///< Maps nicks to "nick_info"
|
|
||||||
|
// TODO: eventually a reference to the server for server buffers
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct buffer *
|
static struct buffer *
|
||||||
buffer_new (void)
|
buffer_new (void)
|
||||||
{
|
{
|
||||||
struct buffer *self = xcalloc (1, sizeof *self);
|
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;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,9 +359,12 @@ buffer_destroy (struct buffer *self)
|
||||||
free (self->name);
|
free (self->name);
|
||||||
// Can't really free "history" here
|
// Can't really free "history" here
|
||||||
free (self->saved_line);
|
free (self->saved_line);
|
||||||
free (self->mode);
|
LIST_FOR_EACH (struct buffer_line, iter, self->lines)
|
||||||
free (self->topic);
|
buffer_line_destroy (iter);
|
||||||
str_map_free (&self->nicks);
|
if (self->user)
|
||||||
|
user_unref (self->user);
|
||||||
|
if (self->channel)
|
||||||
|
channel_unref (self->channel);
|
||||||
free (self);
|
free (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +397,23 @@ struct app_context
|
||||||
SSL_CTX *ssl_ctx; ///< SSL context
|
SSL_CTX *ssl_ctx; ///< SSL context
|
||||||
SSL *ssl; ///< SSL connection
|
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: initialize and update these two values
|
||||||
// TODO: probably issue a USERHOST message for ourselves after connecting
|
// 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
|
struct buffer *buffers_tail; ///< The tail of our buffers
|
||||||
|
|
||||||
// XXX: when we go multiserver, there will be collisions
|
// 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 str_map buffers_by_name; ///< Excludes GLOBAL and SERVER
|
||||||
|
|
||||||
struct buffer *global_buffer; ///< The global buffer
|
struct buffer *global_buffer; ///< The global buffer
|
||||||
|
@ -328,13 +482,22 @@ app_context_init (struct app_context *self)
|
||||||
str_init (&self->read_buffer);
|
str_init (&self->read_buffer);
|
||||||
self->irc_ready = false;
|
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);
|
str_map_init (&self->buffers_by_name);
|
||||||
self->buffers_by_name.key_xfrm = irc_strxfrm;
|
self->buffers_by_name.key_xfrm = irc_strxfrm;
|
||||||
|
|
||||||
self->last_displayed_msg_time = time (NULL);
|
self->last_displayed_msg_time = time (NULL);
|
||||||
|
|
||||||
poller_init (&self->poller);
|
|
||||||
|
|
||||||
char *encoding = nl_langinfo (CODESET);
|
char *encoding = nl_langinfo (CODESET);
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
encoding = xstrdup_printf ("%s//TRANSLIT", encoding);
|
encoding = xstrdup_printf ("%s//TRANSLIT", encoding);
|
||||||
|
@ -370,15 +533,19 @@ app_context_free (struct app_context *self)
|
||||||
if (self->ssl_ctx)
|
if (self->ssl_ctx)
|
||||||
SSL_CTX_free (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_nickname);
|
||||||
free (self->irc_user_mode);
|
free (self->irc_user_mode);
|
||||||
|
|
||||||
|
poller_free (&self->poller);
|
||||||
|
|
||||||
LIST_FOR_EACH (struct buffer, iter, self->buffers)
|
LIST_FOR_EACH (struct buffer, iter, self->buffers)
|
||||||
buffer_destroy (iter);
|
buffer_destroy (iter);
|
||||||
str_map_free (&self->buffers_by_name);
|
str_map_free (&self->buffers_by_name);
|
||||||
|
|
||||||
poller_free (&self->poller);
|
|
||||||
|
|
||||||
iconv_close (self->latin1_to_utf8);
|
iconv_close (self->latin1_to_utf8);
|
||||||
iconv_close (self->term_from_utf8);
|
iconv_close (self->term_from_utf8);
|
||||||
iconv_close (self->term_to_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",
|
str_append_printf (output, "%d:%s",
|
||||||
buffer_get_index (ctx, buffer), buffer->name);
|
buffer_get_index (ctx, buffer), buffer->name);
|
||||||
if (buffer->type == BUFFER_CHANNEL && *buffer->mode)
|
if (buffer->type == BUFFER_CHANNEL && *buffer->channel->mode)
|
||||||
str_append_printf (output, "(%s)", buffer->mode);
|
str_append_printf (output, "(%s)", buffer->channel->mode);
|
||||||
|
|
||||||
if (buffer != ctx->global_buffer)
|
if (buffer != ctx->global_buffer)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue