diff --git a/degesch.c b/degesch.c index d624731..70c6ae0 100644 --- a/degesch.c +++ b/degesch.c @@ -2406,89 +2406,91 @@ irc_process_message (const struct irc_message *msg, // This is the most basic acceptable algorithm; something like ICU with proper // locale specification would be needed to make it work better. +static size_t +wrap_text_for_single_line (const char *text, size_t text_len, + size_t line_len, struct str *output) +{ + int eaten = 0; + + // First try going word by word + const char *word_start; + const char *word_end = text + strcspn (text, " "); + size_t word_len = word_end - text; + while (line_len && word_len <= line_len) + { + if (word_len) + { + str_append_data (output, text, word_len); + + text += word_len; + eaten += word_len; + line_len -= word_len; + } + + // Find the next word's end + word_start = text + strspn (text, " "); + word_end = word_start + strcspn (word_start, " "); + word_len = word_end - text; + } + + if (eaten) + // Discard whitespace between words if split + return eaten + (word_start - text); + + // And if that doesn't help, cut the longest valid block of characters + while (true) + { + const char *next = utf8_next (text, text_len - eaten); + hard_assert (next); + + size_t char_len = next - text; + if (char_len > line_len) + break; + + str_append_data (output, text, char_len); + + text += char_len; + eaten += char_len; + line_len -= char_len; + } + return eaten; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + static bool -wrap_text (const char *message, +wrap_message (const char *message, int line_max, struct str_vector *output, struct error **e) { - // Initialize to the first word, even if it's empty - const char *word_end = message + strcspn (message, " "); + if (line_max <= 0) + goto error; - for (int message_left = strlen (message); message_left; ) + for (size_t message_left = strlen (message); message_left; ) { struct str m; str_init (&m); - int part_left = MIN (line_max, message_left); - bool empty = true; - - // First try going word by word - const char *word_start; - int word_len = word_end - message; - while (part_left && word_len <= part_left) + size_t eaten = wrap_text_for_single_line (message, + MIN ((size_t) line_max, message_left), message_left, &m); + if (!eaten) { - if (word_len) - { - str_append_data (&m, message, word_len); - message += word_len; - message_left -= word_len; - part_left -= word_len; - empty = false; - } - - // Find the next word's end - word_start = message + strspn (message, " "); - word_end = word_start + strcspn (word_start, " "); - word_len = word_end - message; - } - - if (!empty) - { - // Discard whitespace between words if split - message_left -= word_start - message; - message = word_start; - - str_vector_add (output, m.str); str_free (&m); - continue; + goto error; } - // And if that doesn't help, cut the longest valid block of characters. - // Note that we never get to the end of the word, so "word_end" stays. - while (true) - { - const char *next = utf8_next (message, message_left); - hard_assert (next); - - int char_len = next - message; - if (char_len > part_left) - break; - - str_append_data (&m, message, char_len); - - message += char_len; - message_left -= char_len; - part_left -= char_len; - empty = false; - } - - if (!empty) - str_vector_add (output, m.str); - - str_free (&m); - - if (!empty) - continue; - - // Well, that's just weird - error_set (e, - "Message splitting was unsuccessful as there was " - "too little room for UTF-8 characters"); - return false; + str_vector_add_owned (output, str_steal (&m)); + message += eaten; + message_left -= eaten; } return true; -} -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +error: + // Well, that's just weird + error_set (e, + "Message splitting was unsuccessful as there was " + "too little room for UTF-8 characters"); + return false; +} /// Automatically splits messages that arrive at other clients with our prefix /// so that they don't arrive cut off by the server @@ -2507,7 +2509,7 @@ irc_autosplit_message (struct app_context *ctx, const char *message, // However we don't always have the full info for message splitting if (!space_in_one_message) str_vector_add (output, message); - else if (!wrap_text (message, space_in_one_message, output, e)) + else if (!wrap_message (message, space_in_one_message, output, e)) return false; return true; }