From c421532e6e984776ad798d5b8216629e7c73c203 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Thu, 16 Apr 2015 00:56:05 +0200
Subject: degesch: some more progress
Whoa, this thing is huge.
Started implementing the basis for IRC and user command handlers.
---
degesch.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 167 insertions(+), 41 deletions(-)
diff --git a/degesch.c b/degesch.c
index af2ffac..ede96a8 100644
--- a/degesch.c
+++ b/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");
--
cgit v1.2.3-54-g00ecf