From 5538578820550c1a62dc4d0e6451332229eeee75 Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 01:26:33 +0200 Subject: [PATCH 01/14] Move utf8.{h,c} from fe-common/core to core. --- src/core/Makefile.am | 1 + src/{fe-common => }/core/utf8.c | 2 ++ src/{fe-common => }/core/utf8.h | 3 +++ src/{fe-common => }/core/wcwidth.c | 0 src/fe-common/core/Makefile.am | 5 ----- src/fe-common/core/module.h | 2 +- src/fe-text/term.h | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) rename src/{fe-common => }/core/utf8.c (96%) rename src/{fe-common => }/core/utf8.h (93%) rename src/{fe-common => }/core/wcwidth.c (100%) diff --git a/src/core/Makefile.am b/src/core/Makefile.am index fc32e17e..cc200034 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -44,6 +44,7 @@ libcore_a_SOURCES = \ settings.c \ signals.c \ special-vars.c \ + utf8.c \ write-buffer.c structure_headers = \ diff --git a/src/fe-common/core/utf8.c b/src/core/utf8.c similarity index 96% rename from src/fe-common/core/utf8.c rename to src/core/utf8.c index 2d07ea8e..1ab6def9 100644 --- a/src/fe-common/core/utf8.c +++ b/src/core/utf8.c @@ -22,5 +22,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "utf8.h" #include "module.h" +#include "wcwidth.c" diff --git a/src/fe-common/core/utf8.h b/src/core/utf8.h similarity index 93% rename from src/fe-common/core/utf8.h rename to src/core/utf8.h index 3c15dc7d..63261a24 100644 --- a/src/fe-common/core/utf8.h +++ b/src/core/utf8.h @@ -8,6 +8,9 @@ #define is_big5_hi(hi) (0x81 <= (hi) && (hi) <= 0xFE) #define is_big5(hi,lo) (is_big5_hi(hi) && is_big5_lo(lo)) +#include +typedef guint32 unichar; + /* Returns width for character (0-2). */ int mk_wcwidth(unichar c); diff --git a/src/fe-common/core/wcwidth.c b/src/core/wcwidth.c similarity index 100% rename from src/fe-common/core/wcwidth.c rename to src/core/wcwidth.c diff --git a/src/fe-common/core/Makefile.am b/src/fe-common/core/Makefile.am index e755b510..63f91fa6 100644 --- a/src/fe-common/core/Makefile.am +++ b/src/fe-common/core/Makefile.am @@ -24,8 +24,6 @@ libfe_common_core_a_SOURCES = \ fe-queries.c \ fe-server.c \ fe-settings.c \ - utf8.c \ - wcwidth.c \ formats.c \ hilight-text.c \ keyboard.c \ @@ -62,6 +60,3 @@ pkginc_fe_common_core_HEADERS = \ window-items.h \ windows-layout.h \ fe-windows.h - -noinst_HEADERS = \ - utf8.h diff --git a/src/fe-common/core/module.h b/src/fe-common/core/module.h index 51b61b3e..db712ec7 100644 --- a/src/fe-common/core/module.h +++ b/src/fe-common/core/module.h @@ -2,7 +2,7 @@ #define MODULE_NAME "fe-common/core" -typedef guint32 unichar; +#include "utf8.h" typedef struct { time_t time; char *nick; diff --git a/src/fe-text/term.h b/src/fe-text/term.h index 9b726d82..0c7847f6 100644 --- a/src/fe-text/term.h +++ b/src/fe-text/term.h @@ -27,7 +27,7 @@ typedef struct _TERM_WINDOW TERM_WINDOW; #define TERM_TYPE_UTF8 1 #define TERM_TYPE_BIG5 2 -typedef guint32 unichar; +#include "utf8.h" extern TERM_WINDOW *root_window; extern int term_width, term_height; From 5c74a3bb88db0fa32c459f8494e9f5a62a821baa Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 01:39:14 +0200 Subject: [PATCH 02/14] Move advance() from fe-common/core to core. --- src/core/utf8.c | 15 +++++++++++++++ src/core/utf8.h | 5 +++++ src/fe-common/core/formats.c | 16 ---------------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/core/utf8.c b/src/core/utf8.c index 1ab6def9..6daa878a 100644 --- a/src/core/utf8.c +++ b/src/core/utf8.c @@ -26,3 +26,18 @@ #include "module.h" #include "wcwidth.c" +int advance(char const **str, gboolean utf8) +{ + if (utf8) { + gunichar c; + + c = g_utf8_get_char(*str); + *str = g_utf8_next_char(*str); + + return unichar_isprint(c) ? mk_wcwidth(c) : 1; + } else { + *str += 1; + + return 1; + } +} diff --git a/src/core/utf8.h b/src/core/utf8.h index 63261a24..09397f69 100644 --- a/src/core/utf8.h +++ b/src/core/utf8.h @@ -14,6 +14,11 @@ typedef guint32 unichar; /* Returns width for character (0-2). */ int mk_wcwidth(unichar c); +/* Advance the str pointer one character further; return the number of columns + * occupied by the skipped character. + */ +int advance(char const **str, gboolean utf8); + #define unichar_isprint(c) (((c) & ~0x80) >= 32) #define is_utf8_leading(c) (((c) & 0xc0) != 0x80) diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c index ccf48394..7daecfcf 100644 --- a/src/fe-common/core/formats.c +++ b/src/fe-common/core/formats.c @@ -420,22 +420,6 @@ void format_create_dest_tag(TEXT_DEST_REC *dest, void *server, window_find_closest(server, target, level); } -static int advance (char const **str, gboolean utf8) -{ - if (utf8) { - gunichar c; - - c = g_utf8_get_char(*str); - *str = g_utf8_next_char(*str); - - return unichar_isprint(c) ? mk_wcwidth(c) : 1; - } else { - *str += 1; - - return 1; - } -} - /* Return length of text part in string (ie. without % codes) */ int format_get_length(const char *str) { From b0afcc96dbc13cf3a5ed94686bb227ab0d86ceaa Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 01:52:37 +0200 Subject: [PATCH 03/14] Rename advance() into string_advance(). --- src/core/utf8.c | 2 +- src/core/utf8.h | 2 +- src/fe-common/core/formats.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/utf8.c b/src/core/utf8.c index 6daa878a..1daaf03f 100644 --- a/src/core/utf8.c +++ b/src/core/utf8.c @@ -26,7 +26,7 @@ #include "module.h" #include "wcwidth.c" -int advance(char const **str, gboolean utf8) +int string_advance(char const **str, gboolean utf8) { if (utf8) { gunichar c; diff --git a/src/core/utf8.h b/src/core/utf8.h index 09397f69..50ee0886 100644 --- a/src/core/utf8.h +++ b/src/core/utf8.h @@ -17,7 +17,7 @@ int mk_wcwidth(unichar c); /* Advance the str pointer one character further; return the number of columns * occupied by the skipped character. */ -int advance(char const **str, gboolean utf8); +int string_advance(char const **str, gboolean utf8); #define unichar_isprint(c) (((c) & ~0x80) >= 32) #define is_utf8_leading(c) (((c) & 0xc0) != 0x80) diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c index 7daecfcf..def03e10 100644 --- a/src/fe-common/core/formats.c +++ b/src/fe-common/core/formats.c @@ -449,7 +449,7 @@ int format_get_length(const char *str) len++; } - len += advance(&str, utf8); + len += string_advance(&str, utf8); } g_string_free(tmp, TRUE); @@ -491,7 +491,7 @@ int format_real_length(const char *str, int len) } oldstr = str; - len -= advance(&str, utf8); + len -= string_advance(&str, utf8); if (len < 0) str = oldstr; } From 2c8648a9c889fc0a7c4b24367a3f81f08dedccb2 Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 02:10:02 +0200 Subject: [PATCH 04/14] Introduce string_policy(). --- src/core/utf8.c | 13 +++++++++++++ src/core/utf8.h | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/core/utf8.c b/src/core/utf8.c index 1daaf03f..d931ba19 100644 --- a/src/core/utf8.c +++ b/src/core/utf8.c @@ -25,6 +25,8 @@ #include "utf8.h" #include "module.h" #include "wcwidth.c" +/* Provide is_utf8(): */ +#include "recode.h" int string_advance(char const **str, gboolean utf8) { @@ -41,3 +43,14 @@ int string_advance(char const **str, gboolean utf8) return 1; } } + +int string_policy(const char *str) +{ + if (is_utf8()) { + if (!str || g_utf8_validate(str, -1, NULL)) { + /* No string provided or valid UTF-8 string: treat as UTF-8: */ + return TREAT_STRING_AS_UTF8; + } + } + return TREAT_STRING_AS_BYTES; +} diff --git a/src/core/utf8.h b/src/core/utf8.h index 50ee0886..fa11b737 100644 --- a/src/core/utf8.h +++ b/src/core/utf8.h @@ -19,6 +19,20 @@ int mk_wcwidth(unichar c); */ int string_advance(char const **str, gboolean utf8); +/* TREAT_STRING_AS_BYTES means strings are to be treated using strncpy, + * strnlen, etc. + * TREAT_STRING_AS_UTF8 means strings are to be treated using g_utf8_* + * functions. + */ +#define TREAT_STRING_AS_BYTES 0 +#define TREAT_STRING_AS_UTF8 1 + +/* Return how the str string ought to be treated: TREAT_STRING_AS_UTF8 if the + * terminal handles UTF-8 and if the string appears to be a valid UTF-8 string; + * TREAT_STRING_AS_BYTES otherwise. + */ +int string_policy(const char *str); + #define unichar_isprint(c) (((c) & ~0x80) >= 32) #define is_utf8_leading(c) (((c) & 0xc0) != 0x80) From 21c07c006066115af4604e26cd89cf60f94a7d53 Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 02:27:19 +0200 Subject: [PATCH 05/14] Leverage string_policy(). --- src/core/utf8.c | 5 +++-- src/core/utf8.h | 2 +- src/fe-common/core/formats.c | 8 ++++---- src/fe-text/gui-entry.c | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/core/utf8.c b/src/core/utf8.c index d931ba19..c9303d19 100644 --- a/src/core/utf8.c +++ b/src/core/utf8.c @@ -28,9 +28,9 @@ /* Provide is_utf8(): */ #include "recode.h" -int string_advance(char const **str, gboolean utf8) +int string_advance(char const **str, int policy) { - if (utf8) { + if (policy == TREAT_STRING_AS_UTF8) { gunichar c; c = g_utf8_get_char(*str); @@ -38,6 +38,7 @@ int string_advance(char const **str, gboolean utf8) return unichar_isprint(c) ? mk_wcwidth(c) : 1; } else { + /* Assume TREAT_STRING_AS_BYTES: */ *str += 1; return 1; diff --git a/src/core/utf8.h b/src/core/utf8.h index fa11b737..f1a8f0e1 100644 --- a/src/core/utf8.h +++ b/src/core/utf8.h @@ -17,7 +17,7 @@ int mk_wcwidth(unichar c); /* Advance the str pointer one character further; return the number of columns * occupied by the skipped character. */ -int string_advance(char const **str, gboolean utf8); +int string_advance(char const **str, int policy); /* TREAT_STRING_AS_BYTES means strings are to be treated using strncpy, * strnlen, etc. diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c index def03e10..3e88426f 100644 --- a/src/fe-common/core/formats.c +++ b/src/fe-common/core/formats.c @@ -425,12 +425,12 @@ int format_get_length(const char *str) { GString *tmp; int len; - gboolean utf8; + int utf8; int adv = 0; g_return_val_if_fail(str != NULL, 0); - utf8 = is_utf8() && g_utf8_validate(str, -1, NULL); + utf8 = string_policy(str); tmp = g_string_new(NULL); len = 0; @@ -464,12 +464,12 @@ int format_real_length(const char *str, int len) GString *tmp; const char *start; const char *oldstr; - gboolean utf8; + int utf8; int adv = 0; g_return_val_if_fail(str != NULL, 0); g_return_val_if_fail(len >= 0, 0); - utf8 = is_utf8() && g_utf8_validate(str, -1, NULL); + utf8 = string_policy(str); start = str; tmp = g_string_new(NULL); diff --git a/src/fe-text/gui-entry.c b/src/fe-text/gui-entry.c index 63dda34e..beea2273 100644 --- a/src/fe-text/gui-entry.c +++ b/src/fe-text/gui-entry.c @@ -367,7 +367,7 @@ static int scrlen_str(const char *str) g_return_val_if_fail(str != NULL, 0); str = stripped = strip_codes(str); - if (is_utf8() && g_utf8_validate(str, -1, NULL)) { + if (string_policy(str) == TREAT_STRING_AS_UTF8) { while (*str != '\0') { gunichar c; From 35b3ccc6a407f83eba4f0c3787cc5c174bd3385c Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 02:47:26 +0200 Subject: [PATCH 06/14] Introduce string_length() and string_width(). --- src/core/utf8.c | 34 ++++++++++++++++++++++++++++++++++ src/core/utf8.h | 9 +++++++++ 2 files changed, 43 insertions(+) diff --git a/src/core/utf8.c b/src/core/utf8.c index c9303d19..7c75c374 100644 --- a/src/core/utf8.c +++ b/src/core/utf8.c @@ -55,3 +55,37 @@ int string_policy(const char *str) } return TREAT_STRING_AS_BYTES; } + +int string_length(const char *str, int policy) +{ + g_return_val_if_fail(str != NULL, 0); + + if (policy == -1) { + policy = string_policy(str); + } + + if (policy == TREAT_STRING_AS_UTF8) { + return g_utf8_strlen(str, -1); + } + else { + /* Assume TREAT_STRING_AS_BYTES: */ + return strlen(str); + } +} + +int string_width(const char *str, int policy) +{ + int len; + + g_return_val_if_fail(str != NULL, 0); + + if (policy == -1) { + policy = string_policy(str); + } + + len = 0; + while (*str != '\0') { + len += string_advance(&str, policy); + } + return len; +} diff --git a/src/core/utf8.h b/src/core/utf8.h index f1a8f0e1..6f02c5a0 100644 --- a/src/core/utf8.h +++ b/src/core/utf8.h @@ -33,6 +33,15 @@ int string_advance(char const **str, int policy); */ int string_policy(const char *str); +/* Return the length of the str string according to the given policy; if policy + * is -1, this function will call string_policy(). + */ +int string_length(const char *str, int policy); +/* Return the screen width of the str string according to the given policy; if + * policy is -1, this function will call string_policy(). + */ +int string_width(const char *str, int policy); + #define unichar_isprint(c) (((c) & ~0x80) >= 32) #define is_utf8_leading(c) (((c) & 0xc0) != 0x80) From 719efc44a3c3c3db8f12b6ff40d2021837d9a902 Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 03:04:08 +0200 Subject: [PATCH 07/14] Introduce string_chars_for_width(). --- src/core/utf8.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/core/utf8.h | 6 ++++++ 2 files changed, 50 insertions(+) diff --git a/src/core/utf8.c b/src/core/utf8.c index 7c75c374..d1f58599 100644 --- a/src/core/utf8.c +++ b/src/core/utf8.c @@ -89,3 +89,47 @@ int string_width(const char *str, int policy) } return len; } + +int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned int *bytes) +{ + const char *c, *previous_c; + int str_width, char_width, char_count; + + g_return_val_if_fail(str != NULL, -1); + + /* Handle the dummy case where n is 0: */ + if (!n) { + if (bytes) { + *bytes = 0; + } + return 0; + } + + if (policy == -1) { + policy = string_policy(str); + } + + /* Iterate over characters until we reach n: */ + char_count = 0; + str_width = 0; + c = str; + while (*c != '\0') { + previous_c = c; + char_width = string_advance(&c, policy); + if (str_width + char_width > n) { + /* We stepped beyond n, get one step back and stop there: */ + c = previous_c; + break; + } + ++ char_count; + str_width += char_width; + } + /* At this point, we know that char_count characters reach str_width + * columns, which is less than or equal to n. */ + + /* Optionally provide the equivalent amount of bytes: */ + if (bytes) { + *bytes = c - str; + } + return char_count; +} diff --git a/src/core/utf8.h b/src/core/utf8.h index 6f02c5a0..411b2048 100644 --- a/src/core/utf8.h +++ b/src/core/utf8.h @@ -42,6 +42,12 @@ int string_length(const char *str, int policy); */ int string_width(const char *str, int policy); +/* Return the amount of characters from str it takes to reach n columns, or -1 if + * str is NULL. Optionally return the equivalent amount of bytes. + * If policy is -1, this function will call string_policy(). + */ +int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned int *bytes); + #define unichar_isprint(c) (((c) & ~0x80) >= 32) #define is_utf8_leading(c) (((c) & 0xc0) != 0x80) From 09ca3ad48f57b7febd6ad353fb38dbe9234e7e70 Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 03:31:23 +0200 Subject: [PATCH 08/14] Fix indentation of display_sorted_nicks(). This was done assuming an "indent with tab, align with spaces" approach. get_alignment also benefited from a minor indentation fix. --- src/core/special-vars.c | 2 +- src/fe-common/core/fe-channels.c | 64 ++++++++++++++++---------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/core/special-vars.c b/src/core/special-vars.c index 4dcc3d2f..d6794d96 100644 --- a/src/core/special-vars.c +++ b/src/core/special-vars.c @@ -340,7 +340,7 @@ static char *get_alignment(const char *text, int align, int flags, char pad) } ret = str->str; - g_string_free(str, FALSE); + g_string_free(str, FALSE); return ret; } diff --git a/src/fe-common/core/fe-channels.c b/src/fe-common/core/fe-channels.c index 046d641a..fd44be11 100644 --- a/src/fe-common/core/fe-channels.c +++ b/src/fe-common/core/fe-channels.c @@ -328,35 +328,35 @@ static int get_nick_length(void *data) static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) { - WINDOW_REC *window; + WINDOW_REC *window; TEXT_DEST_REC dest; GString *str; GSList *tmp; - char *format, *stripped, *prefix_format; + char *format, *stripped, *prefix_format; char *linebuf, nickmode[2] = { 0, 0 }; int *columns, cols, rows, last_col_rows, col, row, max_width; - int item_extra, linebuf_size, formatnum; + int item_extra, linebuf_size, formatnum; window = window_find_closest(channel->server, channel->visible_name, - MSGLEVEL_CLIENTCRAP); - max_width = window->width; + MSGLEVEL_CLIENTCRAP); + max_width = window->width; - /* get the length of item extra stuff ("[ ] ") */ + /* get the length of item extra stuff ("[ ] ") */ format = format_get_text(MODULE_NAME, NULL, - channel->server, channel->visible_name, - TXT_NAMES_NICK, " ", ""); + channel->server, channel->visible_name, + TXT_NAMES_NICK, " ", ""); stripped = strip_codes(format); item_extra = strlen(stripped); - g_free(stripped); + g_free(stripped); g_free(format); if (settings_get_int("names_max_width") > 0 && settings_get_int("names_max_width") < max_width) max_width = settings_get_int("names_max_width"); - /* remove width of the timestamp from max_width */ + /* remove width of the timestamp from max_width */ format_create_dest(&dest, channel->server, channel->visible_name, - MSGLEVEL_CLIENTCRAP, NULL); + MSGLEVEL_CLIENTCRAP, NULL); format = format_get_line_start(current_theme, &dest, time(NULL)); if (format != NULL) { stripped = strip_codes(format); @@ -365,11 +365,11 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) g_free(format); } - /* remove width of the prefix from max_width */ + /* remove width of the prefix from max_width */ prefix_format = format_get_text(MODULE_NAME, NULL, - channel->server, channel->visible_name, - TXT_NAMES_PREFIX, - channel->visible_name); + channel->server, channel->visible_name, + TXT_NAMES_PREFIX, + channel->visible_name); if (prefix_format != NULL) { stripped = strip_codes(prefix_format); max_width -= strlen(stripped); @@ -384,19 +384,19 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) /* calculate columns */ cols = get_max_column_count(nicklist, get_nick_length, max_width, - settings_get_int("names_max_columns"), - item_extra, 3, &columns, &rows); + settings_get_int("names_max_columns"), + item_extra, 3, &columns, &rows); nicklist = columns_sort_list(nicklist, rows); - /* rows in last column */ + /* rows in last column */ last_col_rows = rows-(cols*rows-g_slist_length(nicklist)); if (last_col_rows == 0) - last_col_rows = rows; + last_col_rows = rows; str = g_string_new(prefix_format); linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size); - col = 0; row = 0; + col = 0; row = 0; for (tmp = nicklist; tmp != NULL; tmp = tmp->next) { NICK_REC *rec = tmp->data; @@ -407,39 +407,39 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) if (linebuf_size < columns[col]-item_extra+1) { linebuf_size = (columns[col]-item_extra+1)*2; - linebuf = g_realloc(linebuf, linebuf_size); + linebuf = g_realloc(linebuf, linebuf_size); } memset(linebuf, ' ', columns[col]-item_extra); linebuf[columns[col]-item_extra] = '\0'; memcpy(linebuf, rec->nick, strlen(rec->nick)); - formatnum = rec->op ? TXT_NAMES_NICK_OP : - rec->halfop ? TXT_NAMES_NICK_HALFOP : - rec->voice ? TXT_NAMES_NICK_VOICE : - TXT_NAMES_NICK; + formatnum = rec->op ? TXT_NAMES_NICK_OP : + rec->halfop ? TXT_NAMES_NICK_HALFOP : + rec->voice ? TXT_NAMES_NICK_VOICE : + TXT_NAMES_NICK; format = format_get_text(MODULE_NAME, NULL, - channel->server, - channel->visible_name, - formatnum, nickmode, linebuf); + channel->server, + channel->visible_name, + formatnum, nickmode, linebuf); g_string_append(str, format); g_free(format); if (++col == cols) { printtext(channel->server, channel->visible_name, - MSGLEVEL_CLIENTCRAP, "%s", str->str); + MSGLEVEL_CLIENTCRAP, "%s", str->str); g_string_truncate(str, 0); if (prefix_format != NULL) - g_string_assign(str, prefix_format); + g_string_assign(str, prefix_format); col = 0; row++; if (row == last_col_rows) - cols--; + cols--; } } if (str->len > strlen(prefix_format)) { printtext(channel->server, channel->visible_name, - MSGLEVEL_CLIENTCRAP, "%s", str->str); + MSGLEVEL_CLIENTCRAP, "%s", str->str); } g_slist_free(nicklist); From 97a4ee78fd08e58fce62c6c0b22de94b625c90f6 Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 03:42:56 +0200 Subject: [PATCH 09/14] get_alignment: handle UTF-8 strings. get_alignment now works with columns (width), not bytes, although it is liable to work with bytes if the given text is not a valid UTF-8 string. --- src/core/special-vars.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/core/special-vars.c b/src/core/special-vars.c index d6794d96..64011b8e 100644 --- a/src/core/special-vars.c +++ b/src/core/special-vars.c @@ -25,6 +25,7 @@ #include "settings.h" #include "servers.h" #include "misc.h" +#include "utf8.h" #define ALIGN_RIGHT 0x01 #define ALIGN_CUT 0x02 @@ -320,18 +321,24 @@ static char *get_alignment(const char *text, int align, int flags, char pad) { GString *str; char *ret; + int policy; + unsigned int cut_bytes; g_return_val_if_fail(text != NULL, NULL); + policy = string_policy(text); + str = g_string_new(text); /* cut */ - if ((flags & ALIGN_CUT) && align > 0 && str->len > align) - g_string_truncate(str, align); + if ((flags & ALIGN_CUT) && align > 0 && string_width(text, policy) > align) { + string_chars_for_width(text, policy, align, &cut_bytes); + g_string_truncate(str, cut_bytes); + } /* add pad characters */ if (flags & ALIGN_PAD) { - while (str->len < align) { + while (string_width(str->str, policy) < align) { if (flags & ALIGN_RIGHT) g_string_prepend_c(str, pad); else From f1b5b515b9f91866d241ba6dbf5e1db6827ff61a Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 03:51:48 +0200 Subject: [PATCH 10/14] Make get_alignment() available outside special-vars.c --- src/core/special-vars.c | 6 +----- src/core/special-vars.h | 7 +++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/core/special-vars.c b/src/core/special-vars.c index 64011b8e..fe6bbed2 100644 --- a/src/core/special-vars.c +++ b/src/core/special-vars.c @@ -27,10 +27,6 @@ #include "misc.h" #include "utf8.h" -#define ALIGN_RIGHT 0x01 -#define ALIGN_CUT 0x02 -#define ALIGN_PAD 0x04 - #define isvarchar(c) \ (i_isalnum(c) || (c) == '_') @@ -317,7 +313,7 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad) } /* return the aligned text */ -static char *get_alignment(const char *text, int align, int flags, char pad) +char *get_alignment(const char *text, int align, int flags, char pad) { GString *str; char *ret; diff --git a/src/core/special-vars.h b/src/core/special-vars.h index 11262dad..300dae0e 100644 --- a/src/core/special-vars.h +++ b/src/core/special-vars.h @@ -9,9 +9,16 @@ #define PARSE_FLAG_ESCAPE_THEME 0x08 /* if any arguments/variables contain { or } chars, escape them with % */ #define PARSE_FLAG_ONLY_ARGS 0x10 /* expand only arguments ($0 $1 etc.) but no other $variables */ +#define ALIGN_RIGHT 0x01 +#define ALIGN_CUT 0x02 +#define ALIGN_PAD 0x04 + typedef char* (*SPECIAL_HISTORY_FUNC) (const char *text, void *item, int *free_ret); +/* Cut and/or pad text so it takes exactly "align" characters on the screen */ +char *get_alignment(const char *text, int align, int flags, char pad); + /* Parse and expand text after '$' character. return value has to be g_free()'d if `free_ret' is TRUE. */ char *parse_special(char **cmd, SERVER_REC *server, void *item, From 29beafcf6fd50ecb8a9f3b2b642c2d29c022a4e3 Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 04:08:15 +0200 Subject: [PATCH 11/14] Improve UTF-8 handling in display_sorted_nicks(). --- src/fe-common/core/fe-channels.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/fe-common/core/fe-channels.c b/src/fe-common/core/fe-channels.c index fd44be11..d87c4ce5 100644 --- a/src/fe-common/core/fe-channels.c +++ b/src/fe-common/core/fe-channels.c @@ -26,6 +26,8 @@ #include "levels.h" #include "misc.h" #include "settings.h" +#include "special-vars.h" +#include "utf8.h" #include "chat-protocols.h" #include "chatnets.h" @@ -323,7 +325,7 @@ static void cmd_channel_remove(const char *data) static int get_nick_length(void *data) { - return strlen(((NICK_REC *) data)->nick); + return string_width(((NICK_REC *) data)->nick, -1); } static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) @@ -333,9 +335,9 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) GString *str; GSList *tmp; char *format, *stripped, *prefix_format; - char *linebuf, nickmode[2] = { 0, 0 }; + char *aligned_nick, nickmode[2] = { 0, 0 }; int *columns, cols, rows, last_col_rows, col, row, max_width; - int item_extra, linebuf_size, formatnum; + int item_extra, formatnum; window = window_find_closest(channel->server, channel->visible_name, MSGLEVEL_CLIENTCRAP); @@ -394,7 +396,6 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) last_col_rows = rows; str = g_string_new(prefix_format); - linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size); col = 0; row = 0; for (tmp = nicklist; tmp != NULL; tmp = tmp->next) { @@ -405,13 +406,9 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) else nickmode[0] = ' '; - if (linebuf_size < columns[col]-item_extra+1) { - linebuf_size = (columns[col]-item_extra+1)*2; - linebuf = g_realloc(linebuf, linebuf_size); - } - memset(linebuf, ' ', columns[col]-item_extra); - linebuf[columns[col]-item_extra] = '\0'; - memcpy(linebuf, rec->nick, strlen(rec->nick)); + aligned_nick = get_alignment(rec->nick, + columns[col]-item_extra, + ALIGN_PAD, ' '); formatnum = rec->op ? TXT_NAMES_NICK_OP : rec->halfop ? TXT_NAMES_NICK_HALFOP : @@ -420,8 +417,9 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) format = format_get_text(MODULE_NAME, NULL, channel->server, channel->visible_name, - formatnum, nickmode, linebuf); + formatnum, nickmode, aligned_nick); g_string_append(str, format); + g_free(aligned_nick); g_free(format); if (++col == cols) { @@ -446,7 +444,6 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist) g_string_free(str, TRUE); g_free_not_null(columns); g_free_not_null(prefix_format); - g_free(linebuf); } void fe_channels_nicklist(CHANNEL_REC *channel, int flags) From 72064de9fe064a7536b501fc470edf75fc393314 Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 04:19:38 +0200 Subject: [PATCH 12/14] Simplify scrlen_str() using string_width(). --- src/fe-text/gui-entry.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/fe-text/gui-entry.c b/src/fe-text/gui-entry.c index beea2273..949a43d3 100644 --- a/src/fe-text/gui-entry.c +++ b/src/fe-text/gui-entry.c @@ -366,22 +366,8 @@ static int scrlen_str(const char *str) char *stripped; g_return_val_if_fail(str != NULL, 0); - str = stripped = strip_codes(str); - if (string_policy(str) == TREAT_STRING_AS_UTF8) { - - while (*str != '\0') { - gunichar c; - - c = g_utf8_get_char(str); - str = g_utf8_next_char(str); - - len += unichar_isprint(c) ? mk_wcwidth(c) : 1; - } - - } else { - len = strlen(str); - } - + stripped = strip_codes(str); + len = string_width(stripped, -1); g_free(stripped); return len; } From a26a387545ae530cb3222fd4daa5ed088e068de2 Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 17:31:11 +0200 Subject: [PATCH 13/14] Adjust some conditions. --- src/core/utf8.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/utf8.c b/src/core/utf8.c index d1f58599..29b277e1 100644 --- a/src/core/utf8.c +++ b/src/core/utf8.c @@ -48,7 +48,7 @@ int string_advance(char const **str, int policy) int string_policy(const char *str) { if (is_utf8()) { - if (!str || g_utf8_validate(str, -1, NULL)) { + if (str == NULL || g_utf8_validate(str, -1, NULL)) { /* No string provided or valid UTF-8 string: treat as UTF-8: */ return TREAT_STRING_AS_UTF8; } @@ -98,8 +98,8 @@ int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned g_return_val_if_fail(str != NULL, -1); /* Handle the dummy case where n is 0: */ - if (!n) { - if (bytes) { + if (n == 0) { + if (bytes != NULL) { *bytes = 0; } return 0; @@ -128,7 +128,7 @@ int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned * columns, which is less than or equal to n. */ /* Optionally provide the equivalent amount of bytes: */ - if (bytes) { + if (bytes != NULL) { *bytes = c - str; } return char_count; From 5d69b4c4a7d334ca29e61969712f5f8eedfa5cd9 Mon Sep 17 00:00:00 2001 From: Xavier G Date: Fri, 13 May 2016 17:35:47 +0200 Subject: [PATCH 14/14] Convert string policies from #define to enum. --- src/core/utf8.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/utf8.h b/src/core/utf8.h index 411b2048..5bb53193 100644 --- a/src/core/utf8.h +++ b/src/core/utf8.h @@ -24,8 +24,10 @@ int string_advance(char const **str, int policy); * TREAT_STRING_AS_UTF8 means strings are to be treated using g_utf8_* * functions. */ -#define TREAT_STRING_AS_BYTES 0 -#define TREAT_STRING_AS_UTF8 1 +enum str_policy { + TREAT_STRING_AS_BYTES, + TREAT_STRING_AS_UTF8 +}; /* Return how the str string ought to be treated: TREAT_STRING_AS_UTF8 if the * terminal handles UTF-8 and if the string appears to be a valid UTF-8 string;