degesch: some more progress
Whoa, this thing is huge. Started implementing the basis for IRC and user command handlers.
This commit is contained in:
parent
08c0027397
commit
c421532e6e
208
degesch.c
208
degesch.c
|
@ -78,6 +78,8 @@ static struct config_item g_config_table[] =
|
|||
{ "socks_username", NULL, "SOCKS auth. username" },
|
||||
{ "socks_password", NULL, "SOCKS auth. password" },
|
||||
|
||||
{ "isolate_buffers", "off", "Isolate global/server buffers" },
|
||||
|
||||
{ ATTR_PROMPT, NULL, "Terminal attributes for the prompt" },
|
||||
{ ATTR_RESET, NULL, "String to reset terminal attributes" },
|
||||
{ ATTR_WARNING, NULL, "Terminal attributes for warnings" },
|
||||
|
@ -244,6 +246,7 @@ struct app_context
|
|||
enum color_mode color_mode; ///< Colour output mode
|
||||
bool reconnect; ///< Whether to reconnect on conn. fail.
|
||||
unsigned long reconnect_delay; ///< Reconnect delay in seconds
|
||||
bool isolate_buffers; ///< Isolate global/server buffers
|
||||
|
||||
// Server connection:
|
||||
|
||||
|
@ -810,7 +813,8 @@ buffer_add (struct app_context *ctx, struct buffer *buffer)
|
|||
str_map_set (&ctx->buffers_by_name, buffer->name, buffer);
|
||||
LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
|
||||
|
||||
// TODO: refresh the prompt? Or caller?
|
||||
// In theory this can't cause changes in the prompt
|
||||
refresh_prompt (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -848,7 +852,7 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer)
|
|||
if (buffer == ctx->server_buffer)
|
||||
ctx->server_buffer = NULL;
|
||||
|
||||
// TODO: refresh the prompt? Or caller?
|
||||
refresh_prompt (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -931,7 +935,7 @@ buffer_activate (struct app_context *ctx, struct buffer *buffer)
|
|||
rl_redisplay ();
|
||||
}
|
||||
|
||||
// TODO: refresh the prompt? Or caller?
|
||||
refresh_prompt (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1395,6 +1399,38 @@ init_readline (void)
|
|||
|
||||
// --- Input handling ----------------------------------------------------------
|
||||
|
||||
// TODO: we will need a proper mode parser; to be shared with kike
|
||||
// TODO: we alse definitely need to parse server capability messages
|
||||
|
||||
static void
|
||||
irc_handle_ping (struct app_context *ctx, const struct irc_message *msg)
|
||||
{
|
||||
if (msg->params.len)
|
||||
irc_send (ctx, "PONG :%s", msg->params.vector[0]);
|
||||
else
|
||||
irc_send (ctx, "PONG");
|
||||
}
|
||||
|
||||
static struct irc_handler
|
||||
{
|
||||
char *name;
|
||||
void (*handler) (struct app_context *ctx, const struct irc_message *msg);
|
||||
}
|
||||
g_irc_handlers[] =
|
||||
{
|
||||
// This list needs to stay sorted
|
||||
// TODO: handle as much as we can
|
||||
{ "PING", irc_handle_ping },
|
||||
};
|
||||
|
||||
static int
|
||||
irc_handler_cmp_by_name (const void *a, const void *b)
|
||||
{
|
||||
const struct irc_handler *first = a;
|
||||
const struct irc_handler *second = b;
|
||||
return strcasecmp_ascii (first->name, second->name);
|
||||
}
|
||||
|
||||
static void
|
||||
irc_process_message (const struct irc_message *msg,
|
||||
const char *raw, void *user_data)
|
||||
|
@ -1414,16 +1450,7 @@ irc_process_message (const struct irc_message *msg,
|
|||
app_readline_restore (&state, ctx->readline_prompt);
|
||||
}
|
||||
|
||||
bool show_to_user = true;
|
||||
if (!strcasecmp (msg->command, "PING"))
|
||||
{
|
||||
show_to_user = false;
|
||||
if (msg->params.len)
|
||||
irc_send (ctx, "PONG :%s", msg->params.vector[0]);
|
||||
else
|
||||
irc_send (ctx, "PONG");
|
||||
}
|
||||
else if (!ctx->irc_ready && (!strcasecmp (msg->command, "MODE")
|
||||
if (!ctx->irc_ready && (!strcasecmp (msg->command, "MODE")
|
||||
|| !strcasecmp (msg->command, "376") // RPL_ENDOFMOTD
|
||||
|| !strcasecmp (msg->command, "422"))) // ERR_NOMOTD
|
||||
{
|
||||
|
@ -1435,29 +1462,56 @@ irc_process_message (const struct irc_message *msg,
|
|||
if (autojoin)
|
||||
irc_send (ctx, "JOIN :%s", autojoin);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: whatever processing we need
|
||||
}
|
||||
|
||||
// This is going to be a lot more complicated
|
||||
if (show_to_user)
|
||||
struct irc_handler key = { .name = msg->command };
|
||||
struct irc_handler *handler = bsearch (&key, g_irc_handlers,
|
||||
N_ELEMENTS (g_irc_handlers), sizeof key, irc_handler_cmp_by_name);
|
||||
if (handler)
|
||||
handler->handler (ctx, msg);
|
||||
|
||||
// Numerics typically have human-readable information
|
||||
unsigned long dummy;
|
||||
if (xstrtoul (&dummy, msg->command, 10))
|
||||
// TODO: ensure proper encoding
|
||||
// FIXME: print to the server buffer
|
||||
print_status ("%s", raw);
|
||||
}
|
||||
|
||||
// TODO: load and preprocess this table so that shortcuts are accepted
|
||||
struct command_handler
|
||||
// --- User input handling -----------------------------------------------------
|
||||
|
||||
static void handle_command_help (struct app_context *, const char *);
|
||||
|
||||
static void
|
||||
handle_command_buffer (struct app_context *ctx, const char *arguments)
|
||||
{
|
||||
// TODO: parse the arguments
|
||||
}
|
||||
|
||||
static void
|
||||
handle_command_quit (struct app_context *ctx, const char *arguments)
|
||||
{
|
||||
if (ctx->irc_fd != -1)
|
||||
{
|
||||
if (*arguments)
|
||||
irc_send (ctx, "QUIT :%s", arguments);
|
||||
else
|
||||
irc_send (ctx, "QUIT :%s", PROGRAM_NAME " " PROGRAM_VERSION);
|
||||
}
|
||||
initiate_quit (ctx);
|
||||
}
|
||||
|
||||
static struct command_handler
|
||||
{
|
||||
char *name;
|
||||
void (*handler) (struct app_context *ctx, const char *arguments);
|
||||
// TODO: probably also a usage string
|
||||
}
|
||||
g_handlers[] =
|
||||
g_command_handlers[] =
|
||||
{
|
||||
{ "buffer", NULL },
|
||||
{ "help", NULL },
|
||||
|
||||
{ "help", handle_command_help },
|
||||
{ "quit", handle_command_quit },
|
||||
{ "buffer", handle_command_buffer },
|
||||
#if 0
|
||||
{ "msg", NULL },
|
||||
{ "query", NULL },
|
||||
{ "notice", NULL },
|
||||
|
@ -1483,17 +1537,93 @@ g_handlers[] =
|
|||
{ "motd", NULL },
|
||||
{ "away", NULL },
|
||||
{ "quote", NULL },
|
||||
{ "quit", NULL },
|
||||
#endif
|
||||
};
|
||||
|
||||
static void
|
||||
process_internal_command (struct app_context *ctx, const char *command)
|
||||
handle_command_help (struct app_context *ctx, const char *arguments)
|
||||
{
|
||||
// TODO: resolve commands from a map
|
||||
// TODO: show a list of all user commands
|
||||
}
|
||||
|
||||
static int
|
||||
command_handler_cmp_by_length (const void *a, const void *b)
|
||||
{
|
||||
const struct command_handler *first = a;
|
||||
const struct command_handler *second = b;
|
||||
return strlen (first->name) - strlen (second->name);
|
||||
}
|
||||
|
||||
static void
|
||||
init_partial_matching_user_command_map (struct str_map *partial)
|
||||
{
|
||||
str_map_init (partial);
|
||||
partial->key_xfrm = tolower_ascii_strxfrm;
|
||||
|
||||
// We process them from the longest to the shortest one,
|
||||
// so that common prefixes favor shorter entries
|
||||
struct command_handler *by_length[N_ELEMENTS (g_command_handlers)];
|
||||
for (size_t i = 0; i < N_ELEMENTS (by_length); i++)
|
||||
by_length[i] = &g_command_handlers[i];
|
||||
qsort (by_length, N_ELEMENTS (by_length), sizeof *by_length,
|
||||
command_handler_cmp_by_length);
|
||||
|
||||
for (size_t i = N_ELEMENTS (by_length); i--; )
|
||||
{
|
||||
char *copy = xstrdup (by_length[i]->name);
|
||||
for (size_t part = strlen (copy); part; part--)
|
||||
{
|
||||
copy[part] = '\0';
|
||||
str_map_set (partial, copy, by_length[i]);
|
||||
}
|
||||
free (copy);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
process_user_command (struct app_context *ctx, char *command)
|
||||
{
|
||||
// Trivially create a partial matching map
|
||||
static bool initialized = false;
|
||||
struct str_map partial;
|
||||
if (!initialized)
|
||||
{
|
||||
init_partial_matching_user_command_map (&partial);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// TODO: cut a single word (strtok_r()?)
|
||||
// TODO: if it's a number, switch to the given buffer
|
||||
|
||||
if (!strcmp (command, "quit"))
|
||||
initiate_quit (ctx);
|
||||
struct command_handler *handler = str_map_find (&partial, command);
|
||||
if (handler)
|
||||
// FIXME: pass arguments correctly
|
||||
handler->handler (ctx, "");
|
||||
}
|
||||
|
||||
static void
|
||||
send_message_to_current_buffer (struct app_context *ctx, char *message)
|
||||
{
|
||||
struct buffer *buffer = ctx->current_buffer;
|
||||
if (!buffer)
|
||||
{
|
||||
// TODO: print an error message to the global buffer
|
||||
return;
|
||||
}
|
||||
|
||||
switch (buffer->type)
|
||||
{
|
||||
case BUFFER_GLOBAL:
|
||||
case BUFFER_SERVER:
|
||||
// TODO: print a message to the buffer that it's not a channel
|
||||
break;
|
||||
case BUFFER_CHANNEL:
|
||||
// TODO: send an IRC message to the channel
|
||||
break;
|
||||
case BUFFER_PM:
|
||||
// TODO: send an IRC message to the user
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1503,19 +1633,12 @@ process_input (struct app_context *ctx, char *user_input)
|
|||
size_t len;
|
||||
|
||||
if (!(input = iconv_xstrdup (ctx->term_to_utf8, user_input, -1, &len)))
|
||||
{
|
||||
print_error ("character conversion failed for `%s'", "user input");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (*input == '/')
|
||||
process_internal_command (ctx, input + 1);
|
||||
else if (input[0] == '/' && input[1] != '/')
|
||||
process_user_command (ctx, input + 1);
|
||||
else
|
||||
{
|
||||
// TODO: send a message to the current buffer
|
||||
}
|
||||
send_message_to_current_buffer (ctx, input);
|
||||
|
||||
fail:
|
||||
free (input);
|
||||
}
|
||||
|
||||
|
@ -2048,7 +2171,10 @@ load_config (struct app_context *ctx, struct error **e)
|
|||
if (!success)
|
||||
return false;
|
||||
|
||||
if (!irc_get_boolean_from_config (ctx, "reconnect", &ctx->reconnect, e))
|
||||
if (!irc_get_boolean_from_config (ctx,
|
||||
"reconnect", &ctx->reconnect, e)
|
||||
|| !irc_get_boolean_from_config (ctx,
|
||||
"isolate_buffers", &ctx->isolate_buffers, e))
|
||||
return false;
|
||||
|
||||
const char *delay_str = str_map_find (&ctx->config, "reconnect_delay");
|
||||
|
|
Loading…
Reference in New Issue