diff --git a/kike.c b/kike.c index 7a2d6e5..2ad92a0 100644 --- a/kike.c +++ b/kike.c @@ -944,23 +944,66 @@ client_set_ping_timer (struct client *c) // --- IRC command handling ---------------------------------------------------- +static void +irc_make_reply (struct client *c, int id, va_list ap, struct str *output) +{ + str_append_printf (output, ":%s %03d %s ", + c->ctx->server_name, id, c->nickname ? c->nickname : ""); + str_append_vprintf (output, + irc_get_text (c->ctx, id, g_default_replies[id]), ap); +} + // XXX: this way we cannot typecheck the arguments, so we must be careful static void irc_send_reply (struct client *c, int id, ...) { - struct str tmp; - str_init (&tmp); + struct str reply; + str_init (&reply); va_list ap; va_start (ap, id); - str_append_printf (&tmp, ":%s %03d %s ", - c->ctx->server_name, id, c->nickname ? c->nickname : ""); - str_append_vprintf (&tmp, - irc_get_text (c->ctx, id, g_default_replies[id]), ap); + irc_make_reply (c, id, ap, &reply); va_end (ap); - client_send_str (c, &tmp); - str_free (&tmp); + client_send_str (c, &reply); + str_free (&reply); +} + +/// Send a space-separated list of words across as many replies as needed +static void +irc_send_reply_vector (struct client *c, int id, char **items, ...) +{ + struct str common; + str_init (&common); + + va_list ap; + va_start (ap, items); + irc_make_reply (c, id, ap, &common); + va_end (ap); + + // We always send at least one message (there might be a client that + // expects us to send this message at least once) + do + { + struct str reply; + str_init (&reply); + str_append_str (&reply, &common); + + // If not even a single item fits in the limit (which may happen, + // in theory) it just gets cropped. We could also skip it. + if (*items) + str_append (&reply, *items++); + + // Append as many items as fits in a single message + while (*items + && reply.len + 1 + strlen (*items) <= IRC_MAX_MESSAGE_LENGTH) + str_append_printf (&reply, " %s", *items++); + + client_send_str (c, &reply); + str_free (&reply); + } + while (*items); + str_free (&common); } #define RETURN_WITH_REPLY(c, ...) \ @@ -1858,13 +1901,8 @@ irc_send_rpl_namreply (struct client *c, const struct channel *chan) str_vector_add_owned (&nicks, str_steal (&result)); } - if (nicks.len) - { - // FIXME: split it into multiple messages if it's too long - char *reply = join_str_vector (&nicks, ' '); - irc_send_reply (c, IRC_RPL_NAMREPLY, type, chan->name, reply); - free (reply); - } + irc_send_reply_vector (c, IRC_RPL_NAMREPLY, + nicks.vector, type, chan->name, ""); str_vector_free (&nicks); } @@ -2024,6 +2062,9 @@ irc_send_whois_reply (struct client *c, const struct client *target) if (target->away_message) irc_send_reply (c, IRC_RPL_AWAY, nick, target->away_message); + struct str_vector channels; + str_vector_init (&channels); + struct str_map_iter iter; str_map_iter_init (&iter, &c->ctx->channels); struct channel *chan; @@ -2040,14 +2081,12 @@ irc_send_whois_reply (struct client *c, const struct client *target) else if (channel_user->modes & IRC_CHAN_MODE_VOICE) str_append_c (&item, '+'); str_append (&item, chan->name); - str_append_c (&item, ' '); - - // TODO: try to merge the results into as few messages as possible - irc_send_reply (c, IRC_RPL_WHOISCHANNELS, nick, item.str); - - str_free (&item); + str_vector_add_owned (&channels, str_steal (&item)); } + irc_send_reply_vector (c, IRC_RPL_WHOISCHANNELS, channels.vector, nick, ""); + str_vector_free (&channels); + irc_send_reply (c, IRC_RPL_ENDOFWHOIS, nick); }