summaryrefslogtreecommitdiff
path: root/degesch.c
diff options
context:
space:
mode:
Diffstat (limited to 'degesch.c')
-rw-r--r--degesch.c839
1 files changed, 397 insertions, 442 deletions
diff --git a/degesch.c b/degesch.c
index ad4abb8..2e6e436 100644
--- a/degesch.c
+++ b/degesch.c
@@ -5681,6 +5681,25 @@ dump_matching_options
// --- User input handling -----------------------------------------------------
+// HANDLER_NEEDS_REG is primarily for message sending commands,
+// as they may want to log buffer lines and use our current nickname
+
+enum handler_flags
+{
+ HANDLER_SERVER = (1 << 0), ///< Server context required
+ HANDLER_NEEDS_REG = (1 << 1), ///< Server registration required
+ HANDLER_CHANNEL_FIRST = (1 << 2), ///< Channel required, first argument
+ HANDLER_CHANNEL_LAST = (1 << 3) ///< Channel required, last argument
+};
+
+struct handler_args
+{
+ struct buffer *buffer; ///< Current buffer
+ struct server *s; ///< Related server
+ const char *channel_name; ///< Related channel name
+ char *arguments; ///< Command arguments
+};
+
/// Cuts the longest non-whitespace portion of text and advances the pointer
static char *
cut_word (char **s)
@@ -5693,8 +5712,11 @@ cut_word (char **s)
return start;
}
+/// Validates a word to be cut from a string
+typedef bool (*word_validator_fn) (void *, char *);
+
static char *
-maybe_cut_word (char **s, bool (*validator) (void *, char *), void *user_data)
+maybe_cut_word (char **s, word_validator_fn validator, void *user_data)
{
char *start = *s;
size_t word_len = strcspn (*s, WORD_BREAKING_CHARS);
@@ -5712,6 +5734,51 @@ maybe_cut_word (char **s, bool (*validator) (void *, char *), void *user_data)
return start;
}
+static char *
+maybe_cut_word_from_end (char **s, word_validator_fn validator, void *user_data)
+{
+ // Find the start and end of the last word
+ char *start = *s, *end = start + strlen (start);
+ while (end > start && strchr (WORD_BREAKING_CHARS, end [-1]))
+ end--;
+ char *word = end;
+ while (word > start && !strchr (WORD_BREAKING_CHARS, word[-1]))
+ word--;
+
+ // There's just one word at maximum, starting at the beginning
+ if (word == start)
+ return maybe_cut_word (s, validator, user_data);
+
+ char *tmp = xstrndup (word, word - start);
+ bool ok = validator (user_data, tmp);
+ free (tmp);
+
+ if (!ok)
+ return NULL;
+
+ // It doesn't start at the beginning, cut it off and return it
+ word[-1] = *end = '\0';
+ return word;
+}
+
+static bool
+validate_channel_name (void *user_data, char *word)
+{
+ return irc_is_channel (user_data, word);
+}
+
+static char *
+try_get_channel (struct handler_args *a,
+ char *(*cutter) (char **, word_validator_fn, void *))
+{
+ char *channel_name = cutter (&a->arguments, validate_channel_name, a->s);
+ if (channel_name)
+ return channel_name;
+ if (a->buffer->type == BUFFER_CHANNEL)
+ return a->buffer->channel->name;
+ return NULL;
+}
+
static bool
try_handle_buffer_goto (struct app_context *ctx, const char *word)
{
@@ -5738,66 +5805,6 @@ try_decode_buffer (struct app_context *ctx, const char *word)
return buffer;
}
-static bool
-server_command_check (struct app_context *ctx, const char *action,
- bool need_registration)
-{
- // "need_registration" is primarily for message sending commands,
- // as they may want to log buffer lines and use our current nickname
-
- if (ctx->current_buffer->type == BUFFER_GLOBAL)
- // XXX: couldn't we just pass the name of the user command here?
- // That doesn't actually concern the function but rather its callers.
- buffer_send_error (ctx, ctx->current_buffer,
- "Can't do this from a global buffer (%s)", action);
- else
- {
- struct server *s = ctx->current_buffer->server;
- if (!irc_is_connected (s))
- buffer_send_error (ctx, s->buffer, "Not connected");
- else if (s->state != IRC_REGISTERED && need_registration)
- buffer_send_error (ctx, s->buffer, "Not registered");
- else
- return true;
- }
- return false;
-}
-
-#define SERVER_COMMAND(name, need_registration) \
- if (!server_command_check (ctx, (name), (need_registration))) \
- return true; \
- struct server *s = ctx->current_buffer->server;
-
-#define CHANNEL_COMMAND(name, need_registration) \
- SERVER_COMMAND ((name), (need_registration)) \
- char *channel_name = try_get_channel (ctx, &arguments); \
- if (!channel_name) \
- { \
- buffer_send_error (ctx, ctx->current_buffer, \
- "Can't %s: %s", (name), \
- "no channel name given and this buffer is not a channel"); \
- return true; \
- }
-
-static bool
-validate_channel_name (void *user_data, char *word)
-{
- struct server *s = user_data;
- return irc_is_channel (s, word);
-}
-
-static char *
-try_get_channel (struct app_context *ctx, char **arguments)
-{
- struct server *s = ctx->current_buffer->server;
- char *channel_name = maybe_cut_word (arguments, validate_channel_name, s);
- if (channel_name)
- return channel_name;
- if (ctx->current_buffer->type == BUFFER_CHANNEL)
- return ctx->current_buffer->channel->name;
- return NULL;
-}
-
static void
show_buffers_list (struct app_context *ctx)
{
@@ -5838,9 +5845,9 @@ handle_buffer_close (struct app_context *ctx, char *arguments)
}
static bool
-handle_command_buffer (struct app_context *ctx, char *arguments)
+handle_command_buffer (struct app_context *ctx, struct handler_args *a)
{
- char *action = cut_word (&arguments);
+ char *action = cut_word (&a->arguments);
if (try_handle_buffer_goto (ctx, action))
return true;
@@ -5858,7 +5865,7 @@ handle_command_buffer (struct app_context *ctx, char *arguments)
// we will probably need to extend liberty for this
}
else if (!strcasecmp_ascii (action, "close"))
- handle_buffer_close (ctx, arguments);
+ handle_buffer_close (ctx, a->arguments);
else
return false;
@@ -6006,11 +6013,11 @@ handle_command_set_assign
}
static bool
-handle_command_set (struct app_context *ctx, char *arguments)
+handle_command_set (struct app_context *ctx, struct handler_args *a)
{
char *option = "*";
- if (*arguments)
- option = cut_word (&arguments);
+ if (*a->arguments)
+ option = cut_word (&a->arguments);
struct str_vector all;
str_vector_init (&all);
@@ -6019,23 +6026,23 @@ handle_command_set (struct app_context *ctx, char *arguments)
bool result = true;
if (!all.len)
buffer_send_error (ctx, ctx->global_buffer, "No matches: %s", option);
- else if (!*arguments)
+ else if (!*a->arguments)
{
buffer_send_status (ctx, ctx->global_buffer, "%s", "");
for (size_t i = 0; i < all.len; i++)
buffer_send_status (ctx, ctx->global_buffer, "%s", all.vector[i]);
}
else
- result = handle_command_set_assign (ctx, &all, arguments);
+ result = handle_command_set_assign (ctx, &all, a->arguments);
str_vector_free (&all);
return result;
}
static bool
-handle_command_save (struct app_context *ctx, char *arguments)
+handle_command_save (struct app_context *ctx, struct handler_args *a)
{
- if (*arguments)
+ if (*a->arguments)
return false;
struct str data;
@@ -6060,104 +6067,93 @@ handle_command_save (struct app_context *ctx, char *arguments)
}
static bool
-handle_command_msg (struct app_context *ctx, char *arguments)
+handle_command_msg (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("send messages", true)
-
- if (!*arguments)
+ if (!*a->arguments)
return false;
- char *target = cut_word (&arguments);
- if (!*arguments)
- buffer_send_error (ctx, s->buffer, "No text to send");
+ char *target = cut_word (&a->arguments);
+ if (!*a->arguments)
+ buffer_send_error (ctx, a->s->buffer, "No text to send");
else
- SEND_AUTOSPLIT_PRIVMSG (s, target, arguments);
+ SEND_AUTOSPLIT_PRIVMSG (a->s, target, a->arguments);
return true;
}
static bool
-handle_command_query (struct app_context *ctx, char *arguments)
+handle_command_query (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("send messages", true)
-
- if (!*arguments)
+ if (!*a->arguments)
return false;
- char *target = cut_word (&arguments);
- if (irc_is_channel (s, target))
- buffer_send_error (ctx, s->buffer, "Cannot query a channel");
- else if (!*arguments)
- buffer_send_error (ctx, s->buffer, "No text to send");
+ char *target = cut_word (&a->arguments);
+ if (irc_is_channel (a->s, target))
+ buffer_send_error (ctx, a->s->buffer, "Cannot query a channel");
+ else if (!*a->arguments)
+ buffer_send_error (ctx, a->s->buffer, "No text to send");
else
{
- buffer_activate (ctx, irc_get_or_make_user_buffer (s, target));
- SEND_AUTOSPLIT_PRIVMSG (s, target, arguments);
+ buffer_activate (ctx, irc_get_or_make_user_buffer (a->s, target));
+ SEND_AUTOSPLIT_PRIVMSG (a->s, target, a->arguments);
}
return true;
}
static bool
-handle_command_notice (struct app_context *ctx, char *arguments)
+handle_command_notice (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("send messages", true)
-
- if (!*arguments)
+ if (!*a->arguments)
return false;
- char *target = cut_word (&arguments);
- if (!*arguments)
- buffer_send_error (ctx, s->buffer, "No text to send");
+ char *target = cut_word (&a->arguments);
+ if (!*a->arguments)
+ buffer_send_error (ctx, a->s->buffer, "No text to send");
else
- SEND_AUTOSPLIT_NOTICE (s, target, arguments);
+ SEND_AUTOSPLIT_NOTICE (a->s, target, a->arguments);
return true;
}
static bool
-handle_command_ctcp (struct app_context *ctx, char *arguments)
+handle_command_ctcp (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("send messages", true)
-
- if (!*arguments)
+ if (!*a->arguments)
return false;
- char *target = cut_word (&arguments);
- if (!*arguments)
+ char *target = cut_word (&a->arguments);
+ if (!*a->arguments)
return false;
- char *tag = cut_word (&arguments);
+ char *tag = cut_word (&a->arguments);
for (char *p = tag; *p; p++)
*p = toupper_ascii (*p);
- if (*arguments)
- irc_send (s, "PRIVMSG %s :\x01%s %s\x01", target, tag, arguments);
+ if (*a->arguments)
+ irc_send (a->s, "PRIVMSG %s :\x01%s %s\x01", target, tag, a->arguments);
else
- irc_send (s, "PRIVMSG %s :\x01%s\x01", target, tag);
+ irc_send (a->s, "PRIVMSG %s :\x01%s\x01", target, tag);
- buffer_send_status (ctx, s->buffer,
- "CTCP query to %s: %s", target, tag);
+ buffer_send_status (ctx, a->s->buffer, "CTCP query to %s: %s", target, tag);
return true;
}
static bool
-handle_command_me (struct app_context *ctx, char *arguments)
-{
- SERVER_COMMAND ("send messages", true)
-
- if (ctx->current_buffer->type == BUFFER_CHANNEL)
- SEND_AUTOSPLIT_ACTION (s,
- ctx->current_buffer->channel->name, arguments);
- else if (ctx->current_buffer->type == BUFFER_PM)
- SEND_AUTOSPLIT_ACTION (s,
- ctx->current_buffer->user->nickname, arguments);
+handle_command_me (struct app_context *ctx, struct handler_args *a)
+{
+ if (a->buffer->type == BUFFER_CHANNEL)
+ SEND_AUTOSPLIT_ACTION (a->s,
+ a->buffer->channel->name, a->arguments);
+ else if (a->buffer->type == BUFFER_PM)
+ SEND_AUTOSPLIT_ACTION (a->s,
+ a->buffer->user->nickname, a->arguments);
else
- buffer_send_error (ctx, s->buffer,
+ buffer_send_error (ctx, a->s->buffer,
"Can't do this from a server buffer (%s)",
"send CTCP actions");
return true;
}
static bool
-handle_command_quit (struct app_context *ctx, char *arguments)
+handle_command_quit (struct app_context *ctx, struct handler_args *a)
{
struct str_map_iter iter;
str_map_iter_init (&iter, &ctx->servers);
@@ -6166,7 +6162,7 @@ handle_command_quit (struct app_context *ctx, char *arguments)
while ((s = str_map_iter_next (&iter)))
{
if (irc_is_connected (s))
- irc_initiate_disconnect (s, *arguments ? arguments : NULL);
+ irc_initiate_disconnect (s, *a->arguments ? a->arguments : NULL);
}
initiate_quit (ctx);
@@ -6174,63 +6170,57 @@ handle_command_quit (struct app_context *ctx, char *arguments)
}
static bool
-handle_command_join (struct app_context *ctx, char *arguments)
+handle_command_join (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("join", true)
-
// XXX: send the last known channel key?
- if (irc_is_channel (s, arguments))
+ if (irc_is_channel (a->s, a->arguments))
// XXX: we may want to split the list of channels
- irc_send (s, "JOIN %s", arguments);
- else if (ctx->current_buffer->type != BUFFER_CHANNEL)
- buffer_send_error (ctx, ctx->current_buffer,
+ irc_send (a->s, "JOIN %s", a->arguments);
+ else if (a->buffer->type != BUFFER_CHANNEL)
+ buffer_send_error (ctx, a->buffer,
"%s: %s", "Can't join",
"no channel name given and this buffer is not a channel");
// TODO: have a better way of checking if we're on the channel
- else if (ctx->current_buffer->channel->users)
- buffer_send_error (ctx, ctx->current_buffer,
- "%s: %s", "Can't join",
- "you already are on the channel");
- else if (*arguments)
- irc_send (s, "JOIN %s :%s",
- ctx->current_buffer->channel->name, arguments);
+ else if (a->buffer->channel->users)
+ buffer_send_error (ctx, a->buffer,
+ "%s: %s", "Can't join", "you already are on the channel");
+ else if (*a->arguments)
+ irc_send (a->s, "JOIN %s :%s", a->buffer->channel->name, a->arguments);
else
- irc_send (s, "JOIN %s", ctx->current_buffer->channel->name);
+ irc_send (a->s, "JOIN %s", a->buffer->channel->name);
return true;
}
static bool
-handle_command_part (struct app_context *ctx, char *arguments)
+handle_command_part (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("part", true)
-
- if (irc_is_channel (s, arguments))
+ if (irc_is_channel (a->s, a->arguments))
{
struct str_vector v;
str_vector_init (&v);
- split_str_ignore_empty (cut_word (&arguments), ' ', &v);
+ split_str_ignore_empty (cut_word (&a->arguments), ' ', &v);
for (size_t i = 0; i < v.len; i++)
{
- if (*arguments)
- irc_send (s, "PART %s :%s", v.vector[i], arguments);
+ // TODO: move this out
+ if (*a->arguments)
+ irc_send (a->s, "PART %s :%s", v.vector[i], a->arguments);
else
- irc_send (s, "PART %s", v.vector[i]);
+ irc_send (a->s, "PART %s", v.vector[i]);
}
str_vector_free (&v);
}
- else if (ctx->current_buffer->type != BUFFER_CHANNEL)
- buffer_send_error (ctx, ctx->current_buffer,
+ else if (a->buffer->type != BUFFER_CHANNEL)
+ buffer_send_error (ctx, a->buffer,
"%s: %s", "Can't part",
"no channel name given and this buffer is not a channel");
// TODO: have a better way of checking if we're on the channel
- else if (!ctx->current_buffer->channel->users)
- buffer_send_error (ctx, ctx->current_buffer,
+ else if (!a->buffer->channel->users)
+ buffer_send_error (ctx, a->buffer,
"%s: %s", "Can't part", "you're not on the channel");
- else if (*arguments)
- irc_send (s, "PART %s :%s",
- ctx->current_buffer->channel->name, arguments);
+ else if (*a->arguments)
+ irc_send (a->s, "PART %s :%s", a->buffer->channel->name, a->arguments);
else
- irc_send (s, "PART %s", ctx->current_buffer->channel->name);
+ irc_send (a->s, "PART %s", a->buffer->channel->name);
return true;
}
@@ -6255,179 +6245,150 @@ cycle_channel (struct server *s, const char *channel_name, const char *reason)
}
static bool
-handle_command_cycle (struct app_context *ctx, char *arguments)
+handle_command_cycle (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("cycle", true)
-
- if (irc_is_channel (s, arguments))
+ if (irc_is_channel (a->s, a->arguments))
{
struct str_vector v;
str_vector_init (&v);
- split_str_ignore_empty (cut_word (&arguments), ' ', &v);
+ split_str_ignore_empty (cut_word (&a->arguments), ' ', &v);
for (size_t i = 0; i < v.len; i++)
- cycle_channel (s, v.vector[i], *arguments ? arguments : NULL);
+ // TODO: just send the arguments, also elsewhere
+ cycle_channel (a->s, v.vector[i],
+ *a->arguments ? a->arguments : NULL);
str_vector_free (&v);
}
- else if (ctx->current_buffer->type != BUFFER_CHANNEL)
- buffer_send_error (ctx, ctx->current_buffer,
+ else if (a->buffer->type != BUFFER_CHANNEL)
+ buffer_send_error (ctx, a->buffer,
"%s: %s", "Can't cycle",
"no channel name given and this buffer is not a channel");
// TODO: have a better way of checking if we're on the channel
- else if (!ctx->current_buffer->channel->users)
- buffer_send_error (ctx, ctx->current_buffer,
+ else if (!a->buffer->channel->users)
+ buffer_send_error (ctx, a->buffer,
"%s: %s", "Can't cycle", "you're not on the channel");
- else if (*arguments)
- cycle_channel (s, ctx->current_buffer->channel->name, arguments);
+ else if (*a->arguments)
+ cycle_channel (a->s, a->buffer->channel->name, a->arguments);
else
- cycle_channel (s, ctx->current_buffer->channel->name, NULL);
- return true;
-}
-
-static void
-mass_channel_mode (struct server *s, const char *channel_name,
- bool adding, char mode_char, struct str_vector *v)
-{
- size_t n;
- for (size_t i = 0; i < v->len; i += n)
- {
- struct str modes; str_init (&modes);
- struct str params; str_init (&params);
-
- n = MIN (v->len - i, s->irc_max_modes);
- str_append_printf (&modes, "MODE %s %c", channel_name, "-+"[adding]);
- for (size_t k = 0; k < n; k++)
- {
- str_append_c (&modes, mode_char);
- str_append_printf (&params, " %s", v->vector[i + k]);
- }
-
- irc_send (s, "%s%s", modes.str, params.str);
-
- str_free (&modes);
- str_free (&params);
- }
-}
-
-static bool
-handle_command_channel_mode (struct app_context *ctx, char *arguments,
- const char *command_name, bool adding, char mode_char)
-{
- CHANNEL_COMMAND (command_name, true)
-
- if (!*arguments)
- return false;
-
- struct str_vector v;
- str_vector_init (&v);
- split_str_ignore_empty (arguments, ' ', &v);
- mass_channel_mode (s, channel_name, adding, mode_char, &v);
- str_vector_free (&v);
+ cycle_channel (a->s, a->buffer->channel->name, NULL);
return true;
}
-#define CHANMODE_HANDLER(name, adding, mode_char) \
- static bool \
- handle_command_ ## name (struct app_context *ctx, char *arguments) \
- { \
- return handle_command_channel_mode \
- (ctx, arguments, (#name), (adding), (mode_char)); \
- }
-
-CHANMODE_HANDLER (op, true, 'o') CHANMODE_HANDLER (deop, false, 'o')
-CHANMODE_HANDLER (voice, true, 'v') CHANMODE_HANDLER (devoice, false, 'v')
-
static bool
-handle_command_mode (struct app_context *ctx, char *arguments)
+handle_command_mode (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("mode", true)
-
// Channel names prefixed by "+" collide with mode strings,
// so we just disallow specifying these channels
char *target = NULL;
- if (strchr ("+-\0", *arguments))
+ if (strchr ("+-\0", *a->arguments))
{
- if (ctx->current_buffer->type == BUFFER_CHANNEL)
- target = ctx->current_buffer->channel->name;
- if (ctx->current_buffer->type == BUFFER_PM)
- target = ctx->current_buffer->user->nickname;
- if (ctx->current_buffer->type == BUFFER_SERVER)
- target = s->irc_user->nickname;
+ if (a->buffer->type == BUFFER_CHANNEL)
+ target = a->buffer->channel->name;
+ if (a->buffer->type == BUFFER_PM)
+ target = a->buffer->user->nickname;
+ if (a->buffer->type == BUFFER_SERVER)
+ target = a->s->irc_user->nickname;
}
else
- // If there arguments and they don't begin with a mode string,
+ // If there a->arguments and they don't begin with a mode string,
// they're either a user name or a channel name
- target = cut_word (&arguments);
+ target = cut_word (&a->arguments);
if (!target)
- buffer_send_error (ctx, ctx->current_buffer,
+ buffer_send_error (ctx, a->buffer,
"%s: %s", "Can't change mode",
"no target given and this buffer is neither a PM nor a channel");
- else if (*arguments)
+ else if (*a->arguments)
// XXX: split channel mode params as necessary using irc_max_modes?
- irc_send (s, "MODE %s %s", target, arguments);
+ irc_send (a->s, "MODE %s %s", target, a->arguments);
else
- irc_send (s, "MODE %s", target);
+ irc_send (a->s, "MODE %s", target);
return true;
}
static bool
-handle_command_topic (struct app_context *ctx, char *arguments)
+handle_command_topic (struct app_context *ctx, struct handler_args *a)
{
- // FIXME: we want "change topic" in the other error message
- CHANNEL_COMMAND ("topic", true)
+ (void) ctx;
- if (*arguments)
+ if (*a->arguments)
// FIXME: there's no way to unset the topic
- irc_send (s, "TOPIC %s :%s", channel_name, arguments);
+ irc_send (a->s, "TOPIC %s :%s", a->channel_name, a->arguments);
else
- irc_send (s, "TOPIC %s", channel_name);
+ irc_send (a->s, "TOPIC %s", a->channel_name);
return true;
}
static bool
-handle_command_kick (struct app_context *ctx, char *arguments)
+handle_command_kick (struct app_context *ctx, struct handler_args *a)
{
- CHANNEL_COMMAND ("kick", true)
+ (void) ctx;
- if (!*arguments)
+ if (!*a->arguments)
return false;
- char *target = cut_word (&arguments);
- if (*arguments)
- irc_send (s, "KICK %s %s :%s", channel_name, target, arguments);
+ char *target = cut_word (&a->arguments);
+ if (*a->arguments)
+ irc_send (a->s, "KICK %s %s :%s",
+ a->channel_name, target, a->arguments);
else
- irc_send (s, "KICK %s %s", channel_name, target);
+ irc_send (a->s, "KICK %s %s", a->channel_name, target);
return true;
}
static bool
-handle_command_kickban (struct app_context *ctx, char *arguments)
+handle_command_kickban (struct app_context *ctx, struct handler_args *a)
{
- CHANNEL_COMMAND ("kickban", true)
+ (void) ctx;
- if (!*arguments)
+ if (!*a->arguments)
return false;
- char *target = cut_word (&arguments);
+ char *target = cut_word (&a->arguments);
if (strpbrk (target, "!@*?"))
return false;
// XXX: how about other masks?
- irc_send (s, "MODE %s +b %s!*@*", channel_name, target);
- if (*arguments)
- irc_send (s, "KICK %s %s :%s", channel_name, target, arguments);
+ irc_send (a->s, "MODE %s +b %s!*@*", a->channel_name, target);
+ if (*a->arguments)
+ irc_send (a->s, "KICK %s %s :%s",
+ a->channel_name, target, a->arguments);
else
- irc_send (s, "KICK %s %s", channel_name, target);
+ irc_send (a->s, "KICK %s %s", a->channel_name, target);
return true;
}
static void
-mass_channel_mode_mask_list (struct server *s, const char *channel_name,
- bool adding, char mode_char, const char *arguments)
+mass_channel_mode (struct server *s, const char *channel_name,
+ bool adding, char mode_char, struct str_vector *v)
+{
+ size_t n;
+ for (size_t i = 0; i < v->len; i += n)
+ {
+ struct str modes; str_init (&modes);
+ struct str params; str_init (&params);
+
+ n = MIN (v->len - i, s->irc_max_modes);
+ str_append_printf (&modes, "MODE %s %c", channel_name, "-+"[adding]);
+ for (size_t k = 0; k < n; k++)
+ {
+ str_append_c (&modes, mode_char);
+ str_append_printf (&params, " %s", v->vector[i + k]);
+ }
+
+ irc_send (s, "%s%s", modes.str, params.str);
+
+ str_free (&modes);
+ str_free (&params);
+ }
+}
+
+static void
+mass_channel_mode_mask_list
+ (struct handler_args *a, bool adding, char mode_char)
{
struct str_vector v;
str_vector_init (&v);
- split_str_ignore_empty (arguments, ' ', &v);
+ split_str_ignore_empty (a->arguments, ' ', &v);
// XXX: this may be a bit too trivial; we could map also nicknames
// to information from WHO polling or userhost-in-names
@@ -6441,86 +6402,68 @@ mass_channel_mode_mask_list (struct server *s, const char *channel_name,
free (target);
}
- mass_channel_mode (s, channel_name, adding, mode_char, &v);
+ mass_channel_mode (a->s, a->channel_name, adding, mode_char, &v);
str_vector_free (&v);
}
static bool
-handle_command_ban (struct app_context *ctx, char *arguments)
+handle_command_ban (struct app_context *ctx, struct handler_args *a)
{
- CHANNEL_COMMAND ("ban", true)
-
- if (!*arguments)
- {
- irc_send (s, "MODE %s +b", channel_name);
- return true;
- }
+ (void) ctx;
- mass_channel_mode_mask_list (s, channel_name, true, 'b', arguments);
+ if (*a->arguments)
+ mass_channel_mode_mask_list (a, true, 'b');
+ else
+ irc_send (a->s, "MODE %s +b", a->channel_name);
return true;
}
static bool
-handle_command_unban (struct app_context *ctx, char *arguments)
+handle_command_unban (struct app_context *ctx, struct handler_args *a)
{
- CHANNEL_COMMAND ("unban", true)
+ (void) ctx;
- if (!*arguments)
+ if (*a->arguments)
+ mass_channel_mode_mask_list (a, false, 'b');
+ else
return false;
-
- mass_channel_mode_mask_list (s, channel_name, false, 'b', arguments);
return true;
}
static bool
-handle_command_invite (struct app_context *ctx, char *arguments)
+handle_command_invite (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("invite", true)
+ (void) ctx;
struct str_vector v;
str_vector_init (&v);
- split_str_ignore_empty (arguments, ' ', &v);
-
- char *channel_name = NULL;
- size_t last = v.len - 1;
- if (v.len && irc_is_channel (s, v.vector[last]))
- channel_name = str_vector_steal (&v, last);
- else if (ctx->current_buffer->type == BUFFER_CHANNEL)
- channel_name = xstrdup (ctx->current_buffer->channel->name);
+ split_str_ignore_empty (a->arguments, ' ', &v);
- bool result = true;
- if (!channel_name)
- buffer_send_error (ctx, ctx->current_buffer,
- "Can't %s: %s", "invite",
- "no channel name given and this buffer is not a channel");
- else if (v.len)
- for (size_t i = 0; i < v.len; i++)
- irc_send (s, "INVITE %s %s", v.vector[i], channel_name);
- else
- result = false;
+ bool result = !!v.len;
+ for (size_t i = 0; i < v.len; i++)
+ irc_send (a->s, "INVITE %s %s", v.vector[i], a->channel_name);
str_vector_free (&v);
- free (channel_name);
return result;
}
static bool
-handle_command_connect (struct app_context *ctx, char *arguments)
+handle_command_connect (struct app_context *ctx, struct handler_args *a)
{
struct server *s = NULL;
- if (*arguments)
+ if (*a->arguments)
{
- char *server_name = cut_word (&arguments);
+ char *server_name = cut_word (&a->arguments);
if (!(s = str_map_find (&ctx->servers, server_name)))
buffer_send_error (ctx, ctx->global_buffer, "%s: %s: %s",
"Can't connect", "no such server", server_name);
}
- else if (ctx->current_buffer->type == BUFFER_GLOBAL)
- buffer_send_error (ctx, ctx->current_buffer,
+ else if (a->buffer->type == BUFFER_GLOBAL)
+ buffer_send_error (ctx, a->buffer,
"%s: %s", "Can't connect",
"no server name given and this buffer is global");
else
- s = ctx->current_buffer->server;
+ s = a->buffer->server;
if (!s)
return true;
@@ -6539,21 +6482,22 @@ handle_command_connect (struct app_context *ctx, char *arguments)
}
static bool
-handle_command_disconnect (struct app_context *ctx, char *arguments)
+handle_command_disconnect (struct app_context *ctx, struct handler_args *a)
{
struct server *s = NULL;
- if (*arguments)
+ if (*a->arguments)
{
- char *server_name = cut_word (&arguments);
+ char *server_name = cut_word (&a->arguments);
if (!(s = str_map_find (&ctx->servers, server_name)))
- buffer_send_error (ctx, ctx->current_buffer, "%s: %s: %s",
+ buffer_send_error (ctx, a->buffer, "%s: %s: %s",
"Can't disconnect", "no such server", server_name);
}
- else if (ctx->current_buffer->type == BUFFER_GLOBAL)
- buffer_send_error (ctx, ctx->current_buffer,
- "%s: %s", "Can't disconnect", "this buffer is global");
+ else if (a->buffer->type == BUFFER_GLOBAL)
+ buffer_send_error (ctx, a->buffer,
+ "%s: %s", "Can't disconnect",
+ "no server name given and this buffer is global");
else
- s = ctx->current_buffer->server;
+ s = a->buffer->server;
if (!s)
return true;
@@ -6566,260 +6510,247 @@ handle_command_disconnect (struct app_context *ctx, char *arguments)
else if (!irc_is_connected (s))
buffer_send_error (ctx, s->buffer, "Not connected");
else
- irc_initiate_disconnect (s, *arguments ? arguments : NULL);
+ irc_initiate_disconnect (s, *a->arguments ? a->arguments : NULL);
return true;
}
static bool
-handle_command_list (struct app_context *ctx, char *arguments)
+handle_command_names (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("list channels", true)
-
- if (*arguments)
- irc_send (s, "LIST %s", arguments);
- else
- irc_send (s, "LIST");
- return true;
-}
-
-static bool
-handle_command_names (struct app_context *ctx, char *arguments)
-{
- SERVER_COMMAND ("names", true)
-
- char *channel_name = try_get_channel (ctx, &arguments);
- if (!channel_name)
- irc_send (s, "NAMES");
- else
- irc_send (s, "NAMES %s", channel_name);
- return true;
-}
-
-static bool
-handle_command_who (struct app_context *ctx, char *arguments)
-{
- SERVER_COMMAND ("who", true)
+ (void) ctx;
- if (*arguments)
- irc_send (s, "WHO %s", arguments);
+ char *channel_name = try_get_channel (a, maybe_cut_word);
+ if (channel_name)
+ irc_send (a->s, "NAMES %s", channel_name);
else
- irc_send (s, "WHO");
+ irc_send (a->s, "NAMES");
return true;
}
static bool
-handle_command_whois (struct app_context *ctx, char *arguments)
-{
- SERVER_COMMAND ("whois", true)
-
- if (*arguments)
- irc_send (s, "WHOIS %s", arguments);
- else if (ctx->current_buffer->type == BUFFER_PM)
- irc_send (s, "WHOIS %s", ctx->current_buffer->user->nickname);
- else if (ctx->current_buffer->type == BUFFER_SERVER)
- irc_send (s, "WHOIS %s", s->irc_user->nickname);
+handle_command_whois (struct app_context *ctx, struct handler_args *a)
+{
+ if (*a->arguments)
+ irc_send (a->s, "WHOIS %s", a->arguments);
+ else if (a->buffer->type == BUFFER_PM)
+ irc_send (a->s, "WHOIS %s", a->buffer->user->nickname);
+ else if (a->buffer->type == BUFFER_SERVER)
+ irc_send (a->s, "WHOIS %s", a->s->irc_user->nickname);
else
- buffer_send_error (ctx, ctx->current_buffer,
+ buffer_send_error (ctx, a->buffer,
"%s: %s", "Can't request info",
"no target given and this buffer is not a PM nor a server");
return true;
}
static bool
-handle_command_whowas (struct app_context *ctx, char *arguments)
+handle_command_whowas (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("whowas", true)
-
- if (*arguments)
- irc_send (s, "WHOWAS %s", arguments);
- else if (ctx->current_buffer->type == BUFFER_PM)
- irc_send (s, "WHOWAS %s", ctx->current_buffer->user->nickname);
+ if (*a->arguments)
+ irc_send (a->s, "WHOWAS %s", a->arguments);
+ else if (a->buffer->type == BUFFER_PM)
+ irc_send (a->s, "WHOWAS %s", a->buffer->user->nickname);
else
- buffer_send_error (ctx, ctx->current_buffer,
+ buffer_send_error (ctx, a->buffer,
"%s: %s", "Can't request info",
"no target given and this buffer is not a PM");
return true;
}
static bool
-handle_command_motd (struct app_context *ctx, char *arguments)
+handle_command_nick (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("motd", true)
+ (void) ctx;
- if (*arguments)
- irc_send (s, "MOTD %s", arguments);
- else
- irc_send (s, "MOTD");
+ if (!*a->arguments)
+ return false;
+
+ irc_send (a->s, "NICK %s", cut_word (&a->arguments));
return true;
}
static bool
-handle_command_stats (struct app_context *ctx, char *arguments)
+handle_command_quote (struct app_context *ctx, struct handler_args *a)
{
- SERVER_COMMAND ("stats", true)
-
- if (*arguments)
- irc_send (s, "STATS %s", arguments);
- else
- irc_send (s, "STATS");
+ (void) ctx;
+ irc_send (a->s, "%s", a->arguments);
return true;
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
static bool
-handle_command_away (struct app_context *ctx, char *arguments)
+handle_command_channel_mode
+ (struct handler_args *a, bool adding, char mode_char)
{
- SERVER_COMMAND ("away", true)
+ if (!*a->arguments)
+ return false;
- if (*arguments)
- irc_send (s, "AWAY %s", arguments);
- else
- irc_send (s, "AWAY");
+ struct str_vector v;
+ str_vector_init (&v);
+ split_str_ignore_empty (a->arguments, ' ', &v);
+ mass_channel_mode (a->s, a->channel_name, adding, mode_char, &v);
+ str_vector_free (&v);
return true;
}
-static bool
-handle_command_nick (struct app_context *ctx, char *arguments)
-{
- SERVER_COMMAND ("change nickname", false)
+#define CHANMODE_HANDLER(name, adding, mode_char) \
+ static bool \
+ handle_command_ ## name (struct app_context *ctx, struct handler_args *a) \
+ { \
+ (void) ctx; \
+ return handle_command_channel_mode (a, (adding), (mode_char)); \
+ }
- if (!*arguments)
- return false;
+CHANMODE_HANDLER (op, true, 'o') CHANMODE_HANDLER (deop, false, 'o')
+CHANMODE_HANDLER (voice, true, 'v') CHANMODE_HANDLER (devoice, false, 'v')
- irc_send (s, "NICK %s", cut_word (&arguments));
- return true;
-}
+#define TRIVIAL_HANDLER(name, command) \
+ static bool \
+ handle_command_ ## name (struct app_context *ctx, struct handler_args *a) \
+ { \
+ (void) ctx; \
+ if (*a->arguments) \
+ irc_send (a->s, #command " %s", a->arguments); \
+ else \
+ irc_send (a->s, #command); \
+ return true; \
+ }
-static bool
-handle_command_quote (struct app_context *ctx, char *arguments)
-{
- SERVER_COMMAND ("quote", false)
- irc_send (s, "%s", arguments);
- return true;
-}
+TRIVIAL_HANDLER (list, "LIST")
+TRIVIAL_HANDLER (who, "WHO")
+TRIVIAL_HANDLER (motd, "MOTD")
+TRIVIAL_HANDLER (stats, "STATS")
+TRIVIAL_HANDLER (away, "AWAY")
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-static bool handle_command_help (struct app_context *, char *);
+static bool handle_command_help (struct app_context *, struct handler_args *);
static struct command_handler
{
const char *name;
const char *description;
const char *usage;
- bool (*handler) (struct app_context *ctx, char *arguments);
+ bool (*handler) (struct app_context *ctx, struct handler_args *a);
+ enum handler_flags flags;
}
g_command_handlers[] =
{
{ "help", "Show help",
"[<command> | <option>]",
- handle_command_help },
+ handle_command_help, 0 },
{ "quit", "Quit the program",
"[<message>]",
- handle_command_quit },
+ handle_command_quit, 0 },
{ "buffer", "Manage buffers",
"list | clear | move | { close [<number> | <name>] } | <number>",
- handle_command_buffer },
+ handle_command_buffer, 0 },
{ "set", "Manage configuration",
"[<option>]",
- handle_command_set },
+ handle_command_set, 0 },
{ "save", "Save configuration",
NULL,
- handle_command_save },
+ handle_command_save, 0 },
{ "msg", "Send message to a nick or channel",
"<target> <message>",
- handle_command_msg },
+ handle_command_msg, HANDLER_SERVER | HANDLER_NEEDS_REG },
{ "query", "Send a private message to a nick",
"<nick> <message>",
- handle_command_query },
+ handle_command_query, HANDLER_SERVER | HANDLER_NEEDS_REG },
{ "notice", "Send notice to a nick or channel",
"<target> <message>",
- handle_command_notice },
+ handle_command_notice, HANDLER_SERVER | HANDLER_NEEDS_REG },
{ "ctcp", "Send a CTCP query",
"<target> <tag>",
- handle_command_ctcp },
+ handle_command_ctcp, HANDLER_SERVER | HANDLER_NEEDS_REG },
{ "me", "Send a CTCP action",
"<message>",
- handle_command_me },
+ handle_command_me, HANDLER_SERVER | HANDLER_NEEDS_REG },
{ "join", "Join channels",
"[<channel>[,<channel>...]] [<key>[,<key>...]]",
- handle_command_join },
+ handle_command_join, HANDLER_SERVER },
{ "part", "Leave channels",
"[<channel>[,<channel>...]] [<reason>]",
- handle_command_part },
+ handle_command_part, HANDLER_SERVER },
{ "cycle", "Rejoin channels",
"[<channel>[,<channel>...]] [<reason>]",
- handle_command_cycle },
+ handle_command_cycle, HANDLER_SERVER },
{ "op", "Give channel operator status",
- "<nick>...", handle_command_op },
+ "<nick>...",
+ handle_command_op, HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
{ "deop", "Remove channel operator status",
- "<nick>...", handle_command_deop },
+ "<nick>...",
+ handle_command_deop, HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
{ "voice", "Give voice",
- "<nick>...", handle_command_voice },
+ "<nick>...",
+ handle_command_voice, HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
{ "devoice", "Remove voice",
- "<nick>...", handle_command_devoice },
+ "<nick>...",
+ handle_command_devoice, HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
{ "mode", "Change mode",
"[<channel>] [<mode>...]",
- handle_command_mode },
+ handle_command_mode, HANDLER_SERVER },
{ "topic", "Change topic",
"[<channel>] [<topic>]",
- handle_command_topic },
+ handle_command_topic, HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
{ "kick", "Kick user from channel",
"[<channel>] <user> [<reason>]",
- handle_command_kick },
+ handle_command_kick, HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
{ "kickban", "Kick and ban user from channel",
"[<channel>] <user> [<reason>]",
- handle_command_kickban },
+ handle_command_kickban, HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
{ "ban", "Ban user from channel",
"[<channel>] [<mask>...]",
- handle_command_ban },
+ handle_command_ban, HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
{ "unban", "Unban user from channel",
"[<channel>] <mask>...",
- handle_command_unban },
+ handle_command_unban, HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
{ "invite", "Invite user to channel",
"<user>... [<channel>]",
- handle_command_invite },
+ handle_command_invite, HANDLER_SERVER | HANDLER_CHANNEL_LAST },
{ "connect", "Connect to the server",
"[<server>]",
- handle_command_connect },
+ handle_command_connect, 0 },
{ "disconnect", "Disconnect from the server",
"[<server> [<reason>]]",
- handle_command_disconnect },
+ handle_command_disconnect, 0 },
{ "list", "List channels and their topic",
"[<channel>[,<channel>...]] [<target>]",
- handle_command_list },
+ handle_command_list, HANDLER_SERVER },
{ "names", "List users on channel",
"[<channel>[,<channel>...]]",
- handle_command_names },
+ handle_command_names, HANDLER_SERVER },
{ "who", "List users",
"[<mask> [o]]",
- handle_command_who },
+ handle_command_who, HANDLER_SERVER },
{ "whois", "Get user information",
"[<target>] <mask>",
- handle_command_whois },
+ handle_command_whois, HANDLER_SERVER },
{ "whowas", "Get user information",
"<user> [<count> [<target>]]",
- handle_command_whowas },
+ handle_command_whowas, HANDLER_SERVER },
{ "motd", "Get the Message of The Day",
"[<target>]",
- handle_command_motd },
+ handle_command_motd, HANDLER_SERVER },
{ "stats", "Query server statistics",
"[<query> [<target>]]",
- handle_command_stats },
+ handle_command_stats, HANDLER_SERVER },
{ "away", "Set away status",
"[<text>]",
- handle_command_away },
+ handle_command_away, HANDLER_SERVER },
{ "nick", "Change current nick",
"<nickname>",
- handle_command_nick },
+ handle_command_nick, HANDLER_SERVER },
{ "quote", "Send a raw command to the server",
"<command>",
- handle_command_quote },
+ handle_command_quote, HANDLER_SERVER },
};
static bool
@@ -6858,9 +6789,9 @@ try_handle_command_help_option (struct app_context *ctx, const char *name)
}
static bool
-handle_command_help (struct app_context *ctx, char *arguments)
+handle_command_help (struct app_context *ctx, struct handler_args *a)
{
- if (!*arguments)
+ if (!*a->arguments)
{
buffer_send_status (ctx, ctx->global_buffer, "%s", "");
buffer_send_status (ctx, ctx->global_buffer, "Commands:");
@@ -6880,7 +6811,7 @@ handle_command_help (struct app_context *ctx, char *arguments)
return true;
}
- char *command = cut_word (&arguments);
+ char *command = cut_word (&a->arguments);
for (size_t i = 0; i < N_ELEMENTS (g_command_handlers); i++)
{
struct command_handler *handler = &g_command_handlers[i];
@@ -6937,7 +6868,7 @@ init_partial_matching_user_command_map (struct str_map *partial)
}
static void
-process_user_command (struct app_context *ctx, char *command)
+process_user_command (struct app_context *ctx, char *input)
{
static bool initialized = false;
static struct str_map partial;
@@ -6947,15 +6878,39 @@ process_user_command (struct app_context *ctx, char *command)
initialized = true;
}
- char *name = cut_word (&command);
- if (try_handle_buffer_goto (ctx, name))
+ char *command_name = cut_word (&input);
+ if (try_handle_buffer_goto (ctx, command_name))
return;
- struct command_handler *handler = str_map_find (&partial, name);
+ struct handler_args args =
+ {
+ .buffer = ctx->current_buffer,
+ .arguments = input,
+ };
+
+ struct command_handler *handler = str_map_find (&partial, command_name);
if (!handler)
buffer_send_error (ctx, ctx->global_buffer,
- "%s: %s", "No such command", name);
- else if (!handler->handler (ctx, command))
+ "%s: %s", "No such command", command_name);
+ else if ((handler->flags & HANDLER_SERVER)
+ && args.buffer->type == BUFFER_GLOBAL)
+ buffer_send_error (ctx, ctx->global_buffer,
+ "/%s: %s", command_name, "can't do this from a global buffer");
+ else if ((handler->flags & HANDLER_SERVER)
+ && !irc_is_connected ((args.s = args.buffer->server)))
+ buffer_send_error (ctx, args.s->buffer, "Not connected");
+ else if ((handler->flags & HANDLER_NEEDS_REG)
+ && args.s->state != IRC_REGISTERED)
+ buffer_send_error (ctx, args.s->buffer, "Not registered");
+ else if (((handler->flags & HANDLER_CHANNEL_FIRST)
+ && !(args.channel_name =
+ try_get_channel (&args, maybe_cut_word)))
+ || ((handler->flags & HANDLER_CHANNEL_LAST)
+ && !(args.channel_name =
+ try_get_channel (&args, maybe_cut_word_from_end))))
+ buffer_send_error (ctx, args.buffer, "/%s: %s", command_name,
+ "no channel name given and this buffer is not a channel");
+ else if (!handler->handler (ctx, &args))
buffer_send_error (ctx, ctx->global_buffer,
"%s: /%s %s", "Usage", handler->name, handler->usage);
}