From e6147fb8f2764392dd685fd8b28f1d69527609cd Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Fri, 13 Jun 2014 06:39:02 +0200 Subject: [PATCH 01/12] Properly split long IRC messages This commit adds handling of long IRC messages to the core. In contrast to the `splitlong.pl' plugin, multi-byte encoded and recoded messages are properly split. To allow for this, a new function has been added to the server struct: `split_message'. `split_message' returns a string array with the message splitted to substrings of a length that the server can handle. If a protocol module doesn't have any limit, it can simply return a singleton array with a copy of the message. The `MSG' chat command now calls `split_message' before `send_message', and emits `message own_public' / `message own_private' with each substring, so that the string splitting will be visible in the UI. `split_message' in the IRC module uses `recode_split' which in turn uses iconv to properly split multi-byte encoded (and recoded) messages. --- src/core/chat-commands.c | 21 +++++++--- src/core/misc.c | 17 ++++++++ src/core/misc.h | 3 ++ src/core/recode.c | 81 ++++++++++++++++++++++++++++++++++++++ src/core/recode.h | 2 + src/core/server-rec.h | 3 ++ src/irc/core/irc-servers.c | 25 ++++++++++++ 7 files changed, 147 insertions(+), 5 deletions(-) diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c index 535bf9f8..e00e6b1a 100644 --- a/src/core/chat-commands.c +++ b/src/core/chat-commands.c @@ -378,12 +378,23 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item) } } if (target != NULL) { - signal_emit("server sendmsg", 4, server, target, msg, - GINT_TO_POINTER(target_type)); + char **splitmsgs = server->split_message(server, target, msg); + char *m; + int n = 0; + + while ((m = splitmsgs[n++])) { + signal_emit("server sendmsg", 4, server, target, m, + GINT_TO_POINTER(target_type)); + signal_emit(target_type == SEND_TARGET_CHANNEL ? + "message own_public" : + "message own_private", 4, server, m, + target, origtarget); + } + g_strfreev(splitmsgs); + } else { + signal_emit("message own_private", 4, server, msg, target, + origtarget); } - signal_emit(target != NULL && target_type == SEND_TARGET_CHANNEL ? - "message own_public" : "message own_private", 4, - server, msg, target, origtarget); if (free_ret && target != NULL) g_free(target); cmd_params_free(free_arg); diff --git a/src/core/misc.c b/src/core/misc.c index 8d821ecc..19789f64 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -966,3 +966,20 @@ char *ascii_strdown(char *str) *s = g_ascii_tolower (*s); return str; } + +char **strsplit_len(const char *str, int len) +{ + char **ret; + int n = strlen(str) / len; + int i; + + if (strlen(str) % len) + n++; + + ret = g_new(char *, n + 1); + for (i = 0; i < n; i++, str += len) + ret[i] = g_strndup(str, len); + ret[n] = NULL; + + return ret; +} diff --git a/src/core/misc.h b/src/core/misc.h index 2cb8e66a..8fb5078f 100644 --- a/src/core/misc.h +++ b/src/core/misc.h @@ -115,4 +115,7 @@ uoff_t str_to_uofft(const char *str); /* find `item' from a space separated `list' */ int find_substr(const char *list, const char *item); +/* split `str' into `len' sized substrings */ +char **strsplit_len(const char *str, int len); + #endif diff --git a/src/core/recode.c b/src/core/recode.c index ca69b73e..2f126d6e 100644 --- a/src/core/recode.c +++ b/src/core/recode.c @@ -182,6 +182,87 @@ char *recode_out(const SERVER_REC *server, const char *str, const char *target) return recoded; } +char **recode_split(const SERVER_REC *server, const char *str, + const char *target, int len) +{ + GIConv cd = (GIConv)-1; + const char *from = translit_charset; + const char *to = translit_charset; + char *translit_to = NULL; + const char *inbuf = str; + const char *previnbuf = inbuf; + char *tmp = NULL; + char *outbuf; + gsize inbytesleft = strlen(inbuf); + gsize outbytesleft = len; + int n = 0; + char **ret; + + if (!str) + return NULL; + + if (settings_get_bool("recode")) { + to = find_conversion(server, target); + if (to == NULL) + /* default outgoing charset if set */ + to = settings_get_str("recode_out_default_charset"); + if (to && *to != '\0') { + if (settings_get_bool("recode_transliterate") && + !is_translit(to)) + to = translit_to = g_strconcat(to, + "//TRANSLIT", + NULL); + } else { + to = from; + } + } + + cd = g_iconv_open(to, from); + if (cd == (GIConv)-1) { + /* Fall back to splitting by byte. */ + ret = strsplit_len(str, len); + goto out; + } + + tmp = g_malloc(outbytesleft); + outbuf = tmp; + ret = g_new(char *, 1); + while (g_iconv(cd, (char **)&inbuf, &inbytesleft, &outbuf, + &outbytesleft) == -1) { + if (errno != E2BIG) { + /* + * Conversion failed. Fall back to splitting + * by byte. + */ + ret[n] = NULL; + g_strfreev(ret); + ret = strsplit_len(str, len); + goto out; + } + + /* Outbuf overflowed, split the input string. */ + ret[n++] = g_strndup(previnbuf, inbuf - previnbuf); + ret = g_renew(char *, ret, n + 1); + previnbuf = inbuf; + + /* Reset outbuf for the next substring. */ + outbuf = tmp; + outbytesleft = len; + } + /* Copy the last substring into the array. */ + ret[n++] = g_strndup(previnbuf, inbuf - previnbuf); + ret = g_renew(char *, ret, n + 1); + ret[n] = NULL; + +out: + if (cd != (GIConv)-1) + g_iconv_close(cd); + g_free(translit_to); + g_free(tmp); + + return ret; +} + void recode_update_charset(void) { const char *charset = settings_get_str("term_charset"); diff --git a/src/core/recode.h b/src/core/recode.h index c8f867cf..b70ec630 100644 --- a/src/core/recode.h +++ b/src/core/recode.h @@ -3,6 +3,8 @@ char *recode_in (const SERVER_REC *server, const char *str, const char *target); char *recode_out (const SERVER_REC *server, const char *str, const char *target); +char **recode_split(const SERVER_REC *server, const char *str, + const char *target, int len); gboolean is_valid_charset(const char *charset); gboolean is_utf8(void); void recode_update_charset(void); diff --git a/src/core/server-rec.h b/src/core/server-rec.h index 726d1c14..69e990c6 100644 --- a/src/core/server-rec.h +++ b/src/core/server-rec.h @@ -61,6 +61,9 @@ const char *(*get_nick_flags)(SERVER_REC *server); /* send public or private message to server */ void (*send_message)(SERVER_REC *server, const char *target, const char *msg, int target_type); +/* split message in case it is too long for the server to receive */ +char **(*split_message)(SERVER_REC *server, const char *target, + const char *msg); /* -- Default implementations are used if NULL -- */ CHANNEL_REC *(*channel_find_func)(SERVER_REC *server, const char *name); diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index e5f86c20..a52fa816 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -102,6 +102,30 @@ static void send_message(SERVER_REC *server, const char *target, g_free(recoded); } +static char **split_message(SERVER_REC *server, const char *target, + const char *msg) +{ + IRC_SERVER_REC *ircserver = IRC_SERVER(server); + int userhostlen = 63; /* Maximum length defined by protocol. */ + + g_return_val_if_fail(ircserver != NULL, NULL); + g_return_val_if_fail(target != NULL, NULL); + g_return_val_if_fail(msg != NULL, NULL); + + /* + * If we have joined a channel, userhost will be set, so we can + * calculate the exact maximum length. + */ + if (ircserver->userhost != NULL) + userhostlen = strlen(ircserver->userhost); + + /* length calculation shamelessly stolen from splitlong.pl */ + return recode_split(SERVER(server), msg, target, + 510 - strlen(":! PRIVMSG :") - + strlen(ircserver->nick) - userhostlen - + strlen(target)); +} + static void server_init(IRC_SERVER_REC *server) { IRC_SERVER_CONNECT_REC *conn; @@ -288,6 +312,7 @@ static void sig_connected(IRC_SERVER_REC *server) server->isnickflag = isnickflag_func; server->ischannel = ischannel_func; + server->split_message = split_message; server->send_message = send_message; server->query_find_func = (QUERY_REC *(*)(SERVER_REC *, const char *)) irc_query_find; From 5c05c854dc298a914de8515a2795fa8dd2adca04 Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Mon, 16 Jun 2014 19:59:12 +0200 Subject: [PATCH 02/12] Add configurable split line prefixes and suffixes Add settings `split_line_start' and `split_line_end' analogous to `splitlong_line_start' and `splitlong_line_end' in `splitlong.pl'. The prefixes and suffixes are concatenated with a wrapper function to keep `recode_split' and `strsplit_len' simple. --- src/irc/core/irc-servers.c | 50 +++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index a52fa816..8af8364d 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -74,6 +74,46 @@ static int ischannel_func(SERVER_REC *server, const char *data) return ischannel(*data); } +static char **split_line(const SERVER_REC *server, const char *line, + const char *target, int len) +{ + const char *start = settings_get_str("split_line_start"); + const char *end = settings_get_str("split_line_end"); + char *recoded_start = recode_out(server, start, target); + char *recoded_end = recode_out(server, end, target); + char **lines; + int i; + + /* + * Having the same length limit on all lines will make the first line + * shorter than necessary if `split_line_start' is set, but it makes + * the code much simpler. It's worth it. + */ + len -= strlen(recoded_start) + strlen(recoded_end); + g_free(recoded_start); + g_free(recoded_end); + if (len <= 0) + return NULL; /* There is no room for anything. */ + + lines = recode_split(server, line, target, len); + for (i = 0; lines[i] != NULL; i++) { + if (i != 0 && *start != '\0') { + /* Not the first line. */ + char *tmp = lines[i]; + lines[i] = g_strconcat(start, tmp, NULL); + g_free(tmp); + } + if (lines[i + 1] != NULL && *end != '\0') { + /* Not the last line. */ + char *tmp = lines[i]; + lines[i] = g_strconcat(tmp, end, NULL); + g_free(tmp); + } + } + + return lines; +} + static void send_message(SERVER_REC *server, const char *target, const char *msg, int target_type) { @@ -120,10 +160,10 @@ static char **split_message(SERVER_REC *server, const char *target, userhostlen = strlen(ircserver->userhost); /* length calculation shamelessly stolen from splitlong.pl */ - return recode_split(SERVER(server), msg, target, - 510 - strlen(":! PRIVMSG :") - - strlen(ircserver->nick) - userhostlen - - strlen(target)); + return split_line(SERVER(server), msg, target, + 510 - strlen(":! PRIVMSG :") - + strlen(ircserver->nick) - userhostlen - + strlen(target)); } static void server_init(IRC_SERVER_REC *server) @@ -893,6 +933,8 @@ void irc_servers_init(void) settings_add_str("misc", "usermode", DEFAULT_USER_MODE); settings_add_time("flood", "cmd_queue_speed", DEFAULT_CMD_QUEUE_SPEED); settings_add_int("flood", "cmds_max_at_once", DEFAULT_CMDS_MAX_AT_ONCE); + settings_add_str("misc", "split_line_start", ""); + settings_add_str("misc", "split_line_end", ""); cmd_tag = -1; From 281c6d437dbc30ffc30c615b8dc773dc937abefa Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Mon, 16 Jun 2014 21:59:48 +0200 Subject: [PATCH 03/12] Avoid unnecessary splitting of lines `split_line_end' could force lines to be unnecessarily split. This commit fixes the problem by making sure that the last line isn't shorter than `split_line_end'. --- src/irc/core/irc-servers.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index 8af8364d..fa7feda6 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -81,7 +81,7 @@ static char **split_line(const SERVER_REC *server, const char *line, const char *end = settings_get_str("split_line_end"); char *recoded_start = recode_out(server, start, target); char *recoded_end = recode_out(server, end, target); - char **lines; + char **lines = NULL; int i; /* @@ -90,10 +90,8 @@ static char **split_line(const SERVER_REC *server, const char *line, * the code much simpler. It's worth it. */ len -= strlen(recoded_start) + strlen(recoded_end); - g_free(recoded_start); - g_free(recoded_end); if (len <= 0) - return NULL; /* There is no room for anything. */ + goto out; /* There is no room for anything. */ lines = recode_split(server, line, target, len); for (i = 0; lines[i] != NULL; i++) { @@ -106,11 +104,36 @@ static char **split_line(const SERVER_REC *server, const char *line, if (lines[i + 1] != NULL && *end != '\0') { /* Not the last line. */ char *tmp = lines[i]; + + if (lines[i + 2] == NULL) { + /* Next to last line. Check if we have room + * to append the last line to the current line, + * to avoid an unnecessary line break. + */ + char *recoded_l = recode_out(server, + lines[i+1], + target); + if (strlen(recoded_l) <= strlen(recoded_end)) { + lines[i] = g_strconcat(tmp, lines[i+1], + NULL); + g_free_and_null(lines[i+1]); + lines = g_renew(char *, lines, i + 2); + + g_free(recoded_l); + g_free(tmp); + break; + } + g_free(recoded_l); + } + lines[i] = g_strconcat(tmp, end, NULL); g_free(tmp); } } +out: + g_free(recoded_start); + g_free(recoded_end); return lines; } From 90f3dd612e0d7a6027e60018ca5476e7ddb52f54 Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Tue, 17 Jun 2014 17:25:40 +0200 Subject: [PATCH 04/12] Fix the `userhostlen' fallback in `split_message' ferret, the author of `splitlong-safe.pl' pointed out that `userhostlen' should not only contain the maximum length of the hostname, but also the maximum length of the username. Now 10 is used as the maximum username length as a fallback. (`splitlong-safe.pl' uses the same limit.) The username limit isn't defined in the standard, but 10 is common on many networks. The odds that something goes wrong here is low, as 1) the fallback limit is only used when the user has not yet joined a channel 2) the maximum hostname length (63) gives some error margin as the hostname usually is shorter --- src/irc/core/irc-servers.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index fa7feda6..872dbbfd 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -169,7 +169,11 @@ static char **split_message(SERVER_REC *server, const char *target, const char *msg) { IRC_SERVER_REC *ircserver = IRC_SERVER(server); - int userhostlen = 63; /* Maximum length defined by protocol. */ + /* + * 63 is the maxmium hostname length defined by the protocol. 10 is + * a common username limit on many networks. 1 is for the `@'. + */ + int userhostlen = 63 + 10 + 1; g_return_val_if_fail(ircserver != NULL, NULL); g_return_val_if_fail(target != NULL, NULL); From bb7bafc93cb78dc185565d5750ac3ca5abff2fc8 Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Tue, 17 Jun 2014 18:17:28 +0200 Subject: [PATCH 05/12] Split long IRC `ACTION' messages Add line splitting logic to commands `/me' and `/action'. --- src/fe-common/irc/fe-irc-commands.c | 27 ++++++++++++++++++++------- src/irc/core/irc-servers.c | 18 ++++++++++++++++++ src/irc/core/irc-servers.h | 5 ++++- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/fe-common/irc/fe-irc-commands.c b/src/fe-common/irc/fe-irc-commands.c index f2b00590..765b5340 100644 --- a/src/fe-common/irc/fe-irc-commands.c +++ b/src/fe-common/irc/fe-irc-commands.c @@ -44,6 +44,9 @@ static void cmd_me(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item) { const char *target; + char *subdata; + char **splitdata; + int n = 0; CMD_IRC_SERVER(server); if (!IS_IRC_ITEM(item)) @@ -53,10 +56,13 @@ static void cmd_me(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item) cmd_return_error(CMDERR_NOT_CONNECTED); target = window_item_get_target(item); - irc_server_send_action(server, target, data); - - signal_emit("message irc own_action", 3, server, data, - item->visible_name); + splitdata = irc_server_split_action(server, target, data); + while ((subdata = splitdata[n++])) { + irc_server_send_action(server, target, subdata); + signal_emit("message irc own_action", 3, server, subdata, + item->visible_name); + } + g_strfreev(splitdata); } /* SYNTAX: ACTION [-] */ @@ -64,6 +70,9 @@ static void cmd_action(const char *data, IRC_SERVER_REC *server) { GHashTable *optlist; const char *target, *text; + char *subtext; + char **splittexts; + int n = 0; void *free_arg; CMD_IRC_SERVER(server); @@ -79,10 +88,14 @@ static void cmd_action(const char *data, IRC_SERVER_REC *server) if (server == NULL || !server->connected) cmd_param_error(CMDERR_NOT_CONNECTED); - irc_server_send_action(server, target, text); - - signal_emit("message irc own_action", 3, server, text, target); + splittexts = irc_server_split_action(server, target, text); + while ((subtext = splittexts[n++])) { + irc_server_send_action(server, target, subtext); + signal_emit("message irc own_action", 3, server, subtext, + target); + } + g_strfreev(splittexts); cmd_params_free(free_arg); } diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index 872dbbfd..dc98e1c1 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -450,6 +450,24 @@ void irc_server_send_action(IRC_SERVER_REC *server, const char *target, const ch g_free(recoded); } +char **irc_server_split_action(IRC_SERVER_REC *server, const char *target, + const char *data) +{ + /* See split_message() on how the maximum length is calculated. */ + int userhostlen = 63 + 10 + 1; + + g_return_val_if_fail(server != NULL, NULL); + g_return_val_if_fail(target != NULL, NULL); + g_return_val_if_fail(data != NULL, NULL); + + if (server->userhost != NULL) + userhostlen = strlen(server->userhost); + + return split_line(SERVER(server), data, target, + 510 - strlen(":! PRIVMSG :\001ACTION \001") - + strlen(server->nick) - userhostlen - strlen(target)); +} + void irc_server_send_away(IRC_SERVER_REC *server, const char *reason) { char *recoded = NULL; diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h index 8c9c1cd7..7e4eeabf 100644 --- a/src/irc/core/irc-servers.h +++ b/src/irc/core/irc-servers.h @@ -117,7 +117,10 @@ void irc_server_purge_output(IRC_SERVER_REC *server, const char *target); char *irc_server_get_channels(IRC_SERVER_REC *server); /* INTERNAL: */ -void irc_server_send_action(IRC_SERVER_REC *server, const char *target, const char *data); +void irc_server_send_action(IRC_SERVER_REC *server, const char *target, + const char *data); +char **irc_server_split_action(IRC_SERVER_REC *server, const char *target, + const char *data); void irc_server_send_away(IRC_SERVER_REC *server, const char *reason); void irc_server_send_data(IRC_SERVER_REC *server, const char *data, int len); void irc_server_init_isupport(IRC_SERVER_REC *server); From 29a49c4688b55b2d62dce1638185707c1ec3d51d Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Thu, 19 Jun 2014 00:50:46 +0200 Subject: [PATCH 06/12] Move the definition of the `split_line' settings --- src/irc/core/irc-servers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index dc98e1c1..85bd207e 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -976,10 +976,10 @@ void irc_server_init_isupport(IRC_SERVER_REC *server) void irc_servers_init(void) { settings_add_str("misc", "usermode", DEFAULT_USER_MODE); - settings_add_time("flood", "cmd_queue_speed", DEFAULT_CMD_QUEUE_SPEED); - settings_add_int("flood", "cmds_max_at_once", DEFAULT_CMDS_MAX_AT_ONCE); settings_add_str("misc", "split_line_start", ""); settings_add_str("misc", "split_line_end", ""); + settings_add_time("flood", "cmd_queue_speed", DEFAULT_CMD_QUEUE_SPEED); + settings_add_int("flood", "cmds_max_at_once", DEFAULT_CMDS_MAX_AT_ONCE); cmd_tag = -1; From 7bfe3a8fa1e9d59f94583390f2904d2f9204b9b5 Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Thu, 19 Jun 2014 01:02:35 +0200 Subject: [PATCH 07/12] Introduce a MAX_USERHOST_LEN constant for IRC --- src/irc/core/irc-servers.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index 85bd207e..e1e79321 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -51,6 +51,12 @@ #define DEFAULT_CMDS_MAX_AT_ONCE 5 #define DEFAULT_MAX_QUERY_CHANS 1 /* more and more IRC networks are using stupid ircds.. */ +/* + * 63 is the maximum hostname length defined by the protocol. 10 is a common + * username limit on many networks. 1 is for the `@'. + */ +#define MAX_USERHOST_LEN (63 + 10 + 1) + void irc_servers_reconnect_init(void); void irc_servers_reconnect_deinit(void); @@ -169,11 +175,7 @@ static char **split_message(SERVER_REC *server, const char *target, const char *msg) { IRC_SERVER_REC *ircserver = IRC_SERVER(server); - /* - * 63 is the maxmium hostname length defined by the protocol. 10 is - * a common username limit on many networks. 1 is for the `@'. - */ - int userhostlen = 63 + 10 + 1; + int userhostlen = MAX_USERHOST_LEN; g_return_val_if_fail(ircserver != NULL, NULL); g_return_val_if_fail(target != NULL, NULL); @@ -453,8 +455,7 @@ void irc_server_send_action(IRC_SERVER_REC *server, const char *target, const ch char **irc_server_split_action(IRC_SERVER_REC *server, const char *target, const char *data) { - /* See split_message() on how the maximum length is calculated. */ - int userhostlen = 63 + 10 + 1; + int userhostlen = MAX_USERHOST_LEN; g_return_val_if_fail(server != NULL, NULL); g_return_val_if_fail(target != NULL, NULL); From ff08b3b0224848bf1c4c4e4e8e73c081570f82c9 Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Thu, 19 Jun 2014 01:11:08 +0200 Subject: [PATCH 08/12] Replace a `goto out' with explicit freeing --- src/irc/core/irc-servers.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index e1e79321..13784f88 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -87,7 +87,7 @@ static char **split_line(const SERVER_REC *server, const char *line, const char *end = settings_get_str("split_line_end"); char *recoded_start = recode_out(server, start, target); char *recoded_end = recode_out(server, end, target); - char **lines = NULL; + char **lines; int i; /* @@ -96,8 +96,12 @@ static char **split_line(const SERVER_REC *server, const char *line, * the code much simpler. It's worth it. */ len -= strlen(recoded_start) + strlen(recoded_end); - if (len <= 0) - goto out; /* There is no room for anything. */ + if (len <= 0) { + /* There is no room for anything. */ + g_free(recoded_start); + g_free(recoded_end); + return NULL; + } lines = recode_split(server, line, target, len); for (i = 0; lines[i] != NULL; i++) { @@ -137,7 +141,6 @@ static char **split_line(const SERVER_REC *server, const char *line, } } -out: g_free(recoded_start); g_free(recoded_end); return lines; From 28a3dbe1641876b71a4805ada9aa25b17e43b1ac Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Thu, 19 Jun 2014 02:24:25 +0200 Subject: [PATCH 09/12] Replace an indent of eight spaces with a tab --- src/core/chat-commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c index e00e6b1a..e8c7e90f 100644 --- a/src/core/chat-commands.c +++ b/src/core/chat-commands.c @@ -386,7 +386,7 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item) signal_emit("server sendmsg", 4, server, target, m, GINT_TO_POINTER(target_type)); signal_emit(target_type == SEND_TARGET_CHANNEL ? - "message own_public" : + "message own_public" : "message own_private", 4, server, m, target, origtarget); } From 695a6a7d9bf8ffbe9378c6aff7c69aa259f2c8c7 Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Sat, 5 Jul 2014 03:58:16 +0200 Subject: [PATCH 10/12] Allow `server.split_message' being NULL Now a module can set `server.split_message = NULL' to disable message splitting, instead of having to implement the function. --- src/core/chat-commands.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c index e8c7e90f..235a9fc4 100644 --- a/src/core/chat-commands.c +++ b/src/core/chat-commands.c @@ -378,10 +378,22 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item) } } if (target != NULL) { - char **splitmsgs = server->split_message(server, target, msg); + char **splitmsgs; + char **tmp = NULL; + char *singlemsg[] = { msg, NULL }; char *m; int n = 0; + /* + * If split_message is NULL, the server doesn't need to split + * long messages. + */ + if (server->split_message != NULL) + splitmsgs = tmp = server->split_message(server, target, + msg); + else + splitmsgs = singlemsg; + while ((m = splitmsgs[n++])) { signal_emit("server sendmsg", 4, server, target, m, GINT_TO_POINTER(target_type)); @@ -390,7 +402,7 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item) "message own_private", 4, server, m, target, origtarget); } - g_strfreev(splitmsgs); + g_strfreev(tmp); } else { signal_emit("message own_private", 4, server, msg, target, origtarget); From 1c73bde23961a3526c4f48e490c3a502f1e429ce Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Sun, 6 Jul 2014 20:31:58 +0200 Subject: [PATCH 11/12] Fix minor coding style issues in message splitting --- src/core/misc.c | 5 +++-- src/core/recode.c | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/misc.c b/src/core/misc.c index 19789f64..32cfeb9b 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -970,10 +970,11 @@ char *ascii_strdown(char *str) char **strsplit_len(const char *str, int len) { char **ret; - int n = strlen(str) / len; + size_t total_len = strlen(str); + int n = total_len / len; int i; - if (strlen(str) % len) + if (total_len % len) n++; ret = g_new(char *, n + 1); diff --git a/src/core/recode.c b/src/core/recode.c index 2f126d6e..029d7ff1 100644 --- a/src/core/recode.c +++ b/src/core/recode.c @@ -198,15 +198,14 @@ char **recode_split(const SERVER_REC *server, const char *str, int n = 0; char **ret; - if (!str) - return NULL; + g_return_val_if_fail(str != NULL, NULL); if (settings_get_bool("recode")) { to = find_conversion(server, target); if (to == NULL) /* default outgoing charset if set */ to = settings_get_str("recode_out_default_charset"); - if (to && *to != '\0') { + if (to != NULL && *to != '\0') { if (settings_get_bool("recode_transliterate") && !is_translit(to)) to = translit_to = g_strconcat(to, From 3257794bf762f67e2fc3e7dee24cdf11a9172631 Mon Sep 17 00:00:00 2001 From: Sebastian Thorarensen Date: Sun, 6 Jul 2014 22:57:00 +0200 Subject: [PATCH 12/12] Add information about message splitting to NEWS (...and AUTHORS.) --- AUTHORS | 1 + NEWS | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/AUTHORS b/AUTHORS index aa4624c6..d2cb58c0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -80,3 +80,4 @@ Other patches (grep for "patch" in ChangeLog) by: Svante Kvarnström Ailin Nemui (Nei) Tom Feist (shabble) + Sebastian Thorarensen diff --git a/NEWS b/NEWS index e9bf9d88..d291c04f 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,11 @@ v0.8.17-head 2014-xx-xx The Irssi team + Performance enhancement of the nicklist as well as the window_item_find function. See Github PR #24. + Disallow unloading of static modules. + Allow UTF-8 characters in /bind. See Github PR #18. + + Split overlong outgoing messages instead of silently truncating them. + Adds two new options: 'split_line_end' and 'split_line_start'. + 'split_line_end' contains a string added to the end of line fragments. + 'split_line_start' contains a string added to the beginning of line + fragments. See Github PR #29. - Fixed various compiler warnings. - Fixed format_get_text Perl API. See Github PR #23. - Fixed gui_printtext_after and term_refresh_*() visibility. See Github PR #22.