From 43d1e58652adc875b6dced525c3d6c544456a52d Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Thu, 30 Apr 2020 21:22:52 +0200 Subject: [PATCH] file_contents: Handle utf-8 characters Fixes #410 --- include/i3status.h | 1 + src/print_file_contents.c | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/include/i3status.h b/include/i3status.h index e0dff2b6..c3bdb5eb 100644 --- a/include/i3status.h +++ b/include/i3status.h @@ -31,6 +31,7 @@ extern char *pct_mark; #define BEGINS_WITH(haystack, needle) (strncmp(haystack, needle, strlen(needle)) == 0) #define max(a, b) ((a) > (b) ? (a) : (b)) +#define min(x, y) ((x) < (y) ? (x) : (y)) #define DEFAULT_SINK_INDEX UINT32_MAX #define COMPOSE_VOLUME_MUTE(vol, mute) ((vol) | ((mute) ? (1 << 30) : 0)) diff --git a/src/print_file_contents.c b/src/print_file_contents.c index 65813f5e..b8cc4d00 100644 --- a/src/print_file_contents.c +++ b/src/print_file_contents.c @@ -24,19 +24,44 @@ static void *scalloc(size_t size) { void print_file_contents(yajl_gen json_gen, char *buffer, const char *title, const char *path, const char *format, const char *format_bad, const int max_chars) { const char *walk = format; + /* Each utf-8 character is a maximum of 4 bytes */ + const int max_bytes = 4 * max_chars; char *outwalk = buffer; - char *buf = scalloc(max_chars * sizeof(char) + 1); + char *buf = scalloc(max_bytes * sizeof(char) + 1); - int n = -1; int fd = open(path, O_RDONLY); INSTANCE(path); if (fd > -1) { - n = read(fd, buf, max_chars); - if (n != -1) { - buf[n] = '\0'; + read(fd, buf, max_bytes); + + int i = 0; + int actual_chars = 0; + while (i < max_bytes) { + const unsigned char c = buf[i]; + + if (c == 0) { + break; + } + /* Count utf-8 characters, by only inspecting the first byte, + * skipping the rest. For reference see: + * https://tools.ietf.org/html/rfc3629#page-4 */ + if (c < 128 /* 0xxxxxxx */) { + i++; + } else if (c < 0xe0 /* 110xxxxx */) { + i += 2; + } else if (c < 0xf0 /* 1110xxxx */) { + i += 3; + } else if (c < 0xf5 /* 11110xxx */) { + i += 4; + } + if (++actual_chars >= max_chars) { + buf[min(i, max_bytes)] = '\0'; + break; + } } + (void)close(fd); START_COLOR("color_good"); } else if (errno != 0) {