summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Marriott <nicm@cvs.openbsd.org>2016-10-11 07:11:41 +0000
committerNicholas Marriott <nicm@cvs.openbsd.org>2016-10-11 07:11:41 +0000
commit32374aba9f526a266212ad87350c6c78bfbc7c33 (patch)
treeb9bf3c060ccde9d5603fe04c02938412c6a70905
parentee47009059623f31cebd95ff4e755100da541d0e (diff)
Support UTF-8 entry into the command prompt.
-rw-r--r--usr.bin/tmux/status.c318
-rw-r--r--usr.bin/tmux/tmux.h6
-rw-r--r--usr.bin/tmux/utf8.c29
3 files changed, 233 insertions, 120 deletions
diff --git a/usr.bin/tmux/status.c b/usr.bin/tmux/status.c
index 84ebf5bfe53..263116aa1aa 100644
--- a/usr.bin/tmux/status.c
+++ b/usr.bin/tmux/status.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: status.c,v 1.151 2016/10/10 21:29:23 nicm Exp $ */
+/* $OpenBSD: status.c,v 1.152 2016/10/11 07:11:40 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -662,18 +662,21 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
struct format_tree *ft;
int keys;
time_t t;
+ char *tmp;
ft = format_create(NULL, 0);
format_defaults(ft, c, NULL, NULL, NULL);
+
t = time(NULL);
+ tmp = format_expand_time(ft, input, t);
status_message_clear(c);
status_prompt_clear(c);
c->prompt_string = format_expand_time(ft, msg, t);
- c->prompt_buffer = format_expand_time(ft, input, t);
- c->prompt_index = strlen(c->prompt_buffer);
+ c->prompt_buffer = utf8_fromcstr(tmp);
+ c->prompt_index = utf8_strlen(c->prompt_buffer);
c->prompt_callbackfn = callbackfn;
c->prompt_freefn = freefn;
@@ -692,6 +695,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
c->flags |= CLIENT_STATUS;
+ free(tmp);
format_free(ft);
}
@@ -723,22 +727,26 @@ status_prompt_update(struct client *c, const char *msg, const char *input)
{
struct format_tree *ft;
time_t t;
+ char *tmp;
ft = format_create(NULL, 0);
format_defaults(ft, c, NULL, NULL, NULL);
+
t = time(NULL);
+ tmp = format_expand_time(ft, input, t);
free(c->prompt_string);
c->prompt_string = format_expand_time(ft, msg, t);
free(c->prompt_buffer);
- c->prompt_buffer = format_expand_time(ft, input, t);
- c->prompt_index = strlen(c->prompt_buffer);
+ c->prompt_buffer = utf8_fromcstr(tmp);
+ c->prompt_index = utf8_strlen(c->prompt_buffer);
c->prompt_hindex = 0;
c->flags |= CLIENT_STATUS;
+ free(tmp);
format_free(ft);
}
@@ -746,57 +754,80 @@ status_prompt_update(struct client *c, const char *msg, const char *input)
int
status_prompt_redraw(struct client *c)
{
- struct screen_write_ctx ctx;
- struct session *s = c->session;
- struct screen old_status;
- size_t i, size, left, len, off;
- struct grid_cell gc;
+ struct screen_write_ctx ctx;
+ struct session *s = c->session;
+ struct screen old_status;
+ u_int i, offset, left, start, pcursor, pwidth, width;
+ struct grid_cell gc, cursorgc;
if (c->tty.sx == 0 || c->tty.sy == 0)
return (0);
memcpy(&old_status, &c->status, sizeof old_status);
screen_init(&c->status, c->tty.sx, 1, 0);
- len = screen_write_strlen("%s", c->prompt_string);
- if (len > c->tty.sx)
- len = c->tty.sx;
- off = 0;
-
- /* Change colours for command mode. */
if (c->prompt_mdata.mode == 1)
style_apply(&gc, s->options, "message-command-style");
else
style_apply(&gc, s->options, "message-style");
- screen_write_start(&ctx, NULL, &c->status);
+ memcpy(&cursorgc, &gc, sizeof cursorgc);
+ cursorgc.attr ^= GRID_ATTR_REVERSE;
+ start = screen_write_strlen("%s", c->prompt_string);
+ if (start > c->tty.sx)
+ start = c->tty.sx;
+
+ screen_write_start(&ctx, NULL, &c->status);
screen_write_cursormove(&ctx, 0, 0);
- screen_write_nputs(&ctx, len, &gc, "%s", c->prompt_string);
+ screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string);
+ while (c->status.cx < screen_size_x(&c->status))
+ screen_write_putc(&ctx, &gc, ' ');
+ screen_write_cursormove(&ctx, start, 0);
- left = c->tty.sx - len;
- if (left != 0) {
- size = screen_write_strlen("%s", c->prompt_buffer);
- if (c->prompt_index >= left) {
- off = c->prompt_index - left + 1;
- if (c->prompt_index == size)
- left--;
- size = left;
+ left = c->tty.sx - start;
+ if (left == 0)
+ goto finished;
+
+ pcursor = utf8_strwidth(c->prompt_buffer, c->prompt_index);
+ pwidth = utf8_strwidth(c->prompt_buffer, -1);
+ if (pcursor >= left) {
+ /*
+ * The cursor would be outside the screen so start drawing
+ * with it on the right.
+ */
+ offset = (pcursor - left) + 1;
+ pwidth = left;
+ } else
+ offset = 0;
+ if (pwidth > left)
+ pwidth = left;
+
+ width = 0;
+ for (i = 0; c->prompt_buffer[i].size != 0; i++) {
+ if (width < offset) {
+ width += c->prompt_buffer[i].width;
+ continue;
}
- screen_write_nputs(&ctx, left, &gc, "%s", c->prompt_buffer +
- off);
+ if (width >= offset + pwidth)
+ break;
+ width += c->prompt_buffer[i].width;
+ if (width > offset + pwidth)
+ break;
- for (i = len + size; i < c->tty.sx; i++)
- screen_write_putc(&ctx, &gc, ' ');
+ if (i != c->prompt_index) {
+ utf8_copy(&gc.data, &c->prompt_buffer[i]);
+ screen_write_cell(&ctx, &gc);
+ } else {
+ utf8_copy(&cursorgc.data, &c->prompt_buffer[i]);
+ screen_write_cell(&ctx, &cursorgc);
+ }
}
+ if (c->status.cx < screen_size_x(&c->status) && c->prompt_index >= i)
+ screen_write_putc(&ctx, &cursorgc, ' ');
+finished:
screen_write_stop(&ctx);
- /* Apply fake cursor. */
- off = len + c->prompt_index - off;
- grid_view_get_cell(c->status.grid, off, 0, &gc);
- gc.attr ^= GRID_ATTR_REVERSE;
- grid_view_set_cell(c->status.grid, off, 0, &gc);
-
if (grid_compare(c->status.grid, old_status.grid) == 0) {
screen_free(&old_status);
return (0);
@@ -805,19 +836,37 @@ status_prompt_redraw(struct client *c)
return (1);
}
+/* Is this a separator? */
+static int
+status_prompt_in_list(const char *ws, const struct utf8_data *ud)
+{
+ if (ud->size != 1 || ud->width != 1)
+ return (0);
+ return (strchr(ws, *ud->data) != NULL);
+}
+
+/* Is this a space? */
+static int
+status_prompt_space(const struct utf8_data *ud)
+{
+ if (ud->size != 1 || ud->width != 1)
+ return (0);
+ return (*ud->data == ' ');
+}
+
/* Handle keys in prompt. */
void
status_prompt_key(struct client *c, key_code key)
{
- struct session *sess = c->session;
- struct options *oo = sess->options;
+ struct options *oo = c->session->options;
struct paste_buffer *pb;
- char *s, *first, *last, word[64], swapc;
- const char *histstr, *bufdata, *wsep = NULL;
+ char *s, word[64];
+ const char *histstr, *bufdata, *ws = NULL;
u_char ch;
- size_t size, n, off, idx, bufsize;
+ size_t size, n, off, idx, bufsize, used;
+ struct utf8_data tmp, *first, *last, *ud;
- size = strlen(c->prompt_buffer);
+ size = utf8_strlen(c->prompt_buffer);
switch (mode_key_lookup(&c->prompt_mdata, key, NULL, NULL)) {
case MODEKEYEDIT_CURSORLEFT:
if (c->prompt_index > 0) {
@@ -856,7 +905,7 @@ status_prompt_key(struct client *c, key_code key)
}
break;
case MODEKEYEDIT_COMPLETE:
- if (*c->prompt_buffer == '\0')
+ if (c->prompt_buffer[0].size == 0)
break;
idx = c->prompt_index;
@@ -864,40 +913,50 @@ status_prompt_key(struct client *c, key_code key)
idx--;
/* Find the word we are in. */
- first = c->prompt_buffer + idx;
- while (first > c->prompt_buffer && *first != ' ')
+ first = &c->prompt_buffer[idx];
+ while (first > c->prompt_buffer && !status_prompt_space(first))
first--;
- while (*first == ' ')
+ while (first->size != 0 && status_prompt_space(first))
first++;
- last = c->prompt_buffer + idx;
- while (*last != '\0' && *last != ' ')
+ last = &c->prompt_buffer[idx];
+ while (last->size != 0 && !status_prompt_space(last))
last++;
- while (*last == ' ')
+ while (last > c->prompt_buffer && status_prompt_space(last))
last--;
- if (*last != '\0')
+ if (last->size != 0)
last++;
- if (last <= first ||
- ((size_t) (last - first)) > (sizeof word) - 1)
+ if (last <= first)
break;
- memcpy(word, first, last - first);
- word[last - first] = '\0';
+
+ used = 0;
+ for (ud = first; ud < last; ud++) {
+ if (used + ud->size >= sizeof word)
+ break;
+ memcpy(word + used, ud->data, ud->size);
+ used += ud->size;
+ }
+ if (ud != last)
+ break;
+ word[used] = '\0';
/* And try to complete it. */
- if ((s = status_prompt_complete(sess, word)) == NULL)
+ if ((s = status_prompt_complete(c->session, word)) == NULL)
break;
/* Trim out word. */
n = size - (last - c->prompt_buffer) + 1; /* with \0 */
- memmove(first, last, n);
+ memmove(first, last, n * sizeof *c->prompt_buffer);
size -= last - first;
/* Insert the new word. */
size += strlen(s);
off = first - c->prompt_buffer;
- c->prompt_buffer = xrealloc(c->prompt_buffer, size + 1);
+ c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1,
+ sizeof *c->prompt_buffer);
first = c->prompt_buffer + off;
- memmove(first + strlen(s), first, n);
- memcpy(first, s, strlen(s));
+ memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer);
+ for (idx = 0; idx < strlen(s); idx++)
+ utf8_set(&first[idx], s[idx]);
c->prompt_index = (first - c->prompt_buffer) + strlen(s);
free(s);
@@ -907,11 +966,12 @@ status_prompt_key(struct client *c, key_code key)
case MODEKEYEDIT_BACKSPACE:
if (c->prompt_index != 0) {
if (c->prompt_index == size)
- c->prompt_buffer[--c->prompt_index] = '\0';
+ c->prompt_buffer[--c->prompt_index].size = 0;
else {
memmove(c->prompt_buffer + c->prompt_index - 1,
c->prompt_buffer + c->prompt_index,
- size + 1 - c->prompt_index);
+ (size + 1 - c->prompt_index) *
+ sizeof *c->prompt_buffer);
c->prompt_index--;
}
c->flags |= CLIENT_STATUS;
@@ -922,38 +982,39 @@ status_prompt_key(struct client *c, key_code key)
if (c->prompt_index != size) {
memmove(c->prompt_buffer + c->prompt_index,
c->prompt_buffer + c->prompt_index + 1,
- size + 1 - c->prompt_index);
+ (size + 1 - c->prompt_index) *
+ sizeof *c->prompt_buffer);
c->flags |= CLIENT_STATUS;
}
break;
case MODEKEYEDIT_DELETELINE:
case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE:
- *c->prompt_buffer = '\0';
+ c->prompt_buffer[0].size = 0;
c->prompt_index = 0;
c->flags |= CLIENT_STATUS;
break;
case MODEKEYEDIT_DELETETOENDOFLINE:
case MODEKEYEDIT_SWITCHMODECHANGELINE:
if (c->prompt_index < size) {
- c->prompt_buffer[c->prompt_index] = '\0';
+ c->prompt_buffer[c->prompt_index].size = 0;
c->flags |= CLIENT_STATUS;
}
break;
case MODEKEYEDIT_DELETEWORD:
- wsep = options_get_string(oo, "word-separators");
+ ws = options_get_string(oo, "word-separators");
idx = c->prompt_index;
/* Find a non-separator. */
while (idx != 0) {
idx--;
- if (!strchr(wsep, c->prompt_buffer[idx]))
+ if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break;
}
/* Find the separator at the beginning of the word. */
while (idx != 0) {
idx--;
- if (strchr(wsep, c->prompt_buffer[idx])) {
+ if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
/* Go back to the word. */
idx++;
break;
@@ -962,53 +1023,55 @@ status_prompt_key(struct client *c, key_code key)
memmove(c->prompt_buffer + idx,
c->prompt_buffer + c->prompt_index,
- size + 1 - c->prompt_index);
+ (size + 1 - c->prompt_index) *
+ sizeof *c->prompt_buffer);
memset(c->prompt_buffer + size - (c->prompt_index - idx),
- '\0', c->prompt_index - idx);
+ '\0', (c->prompt_index - idx) * sizeof *c->prompt_buffer);
c->prompt_index = idx;
+
c->flags |= CLIENT_STATUS;
break;
case MODEKEYEDIT_NEXTSPACE:
- wsep = " ";
+ ws = " ";
/* FALLTHROUGH */
case MODEKEYEDIT_NEXTWORD:
- if (wsep == NULL)
- wsep = options_get_string(oo, "word-separators");
+ if (ws == NULL)
+ ws = options_get_string(oo, "word-separators");
/* Find a separator. */
while (c->prompt_index != size) {
- c->prompt_index++;
- if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
+ idx = ++c->prompt_index;
+ if (status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break;
}
- /* Find the word right after the separation. */
+ /* Find the word right after the separator. */
while (c->prompt_index != size) {
- c->prompt_index++;
- if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
+ idx = ++c->prompt_index;
+ if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break;
}
c->flags |= CLIENT_STATUS;
break;
case MODEKEYEDIT_NEXTSPACEEND:
- wsep = " ";
+ ws = " ";
/* FALLTHROUGH */
case MODEKEYEDIT_NEXTWORDEND:
- if (wsep == NULL)
- wsep = options_get_string(oo, "word-separators");
+ if (ws == NULL)
+ ws = options_get_string(oo, "word-separators");
/* Find a word. */
while (c->prompt_index != size) {
- c->prompt_index++;
- if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
+ idx = ++c->prompt_index;
+ if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break;
}
/* Find the separator at the end of the word. */
while (c->prompt_index != size) {
- c->prompt_index++;
- if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
+ idx = ++c->prompt_index;
+ if (status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break;
}
@@ -1020,23 +1083,23 @@ status_prompt_key(struct client *c, key_code key)
c->flags |= CLIENT_STATUS;
break;
case MODEKEYEDIT_PREVIOUSSPACE:
- wsep = " ";
+ ws = " ";
/* FALLTHROUGH */
case MODEKEYEDIT_PREVIOUSWORD:
- if (wsep == NULL)
- wsep = options_get_string(oo, "word-separators");
+ if (ws == NULL)
+ ws = options_get_string(oo, "word-separators");
/* Find a non-separator. */
while (c->prompt_index != 0) {
- c->prompt_index--;
- if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
+ idx = --c->prompt_index;
+ if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break;
}
/* Find the separator at the beginning of the word. */
while (c->prompt_index != 0) {
- c->prompt_index--;
- if (strchr(wsep, c->prompt_buffer[c->prompt_index])) {
+ idx = --c->prompt_index;
+ if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
/* Go back to the word. */
c->prompt_index++;
break;
@@ -1050,8 +1113,8 @@ status_prompt_key(struct client *c, key_code key)
if (histstr == NULL)
break;
free(c->prompt_buffer);
- c->prompt_buffer = xstrdup(histstr);
- c->prompt_index = strlen(c->prompt_buffer);
+ c->prompt_buffer = utf8_fromcstr(histstr);
+ c->prompt_index = utf8_strlen(c->prompt_buffer);
c->flags |= CLIENT_STATUS;
break;
case MODEKEYEDIT_HISTORYDOWN:
@@ -1059,8 +1122,8 @@ status_prompt_key(struct client *c, key_code key)
if (histstr == NULL)
break;
free(c->prompt_buffer);
- c->prompt_buffer = xstrdup(histstr);
- c->prompt_index = strlen(c->prompt_buffer);
+ c->prompt_buffer = utf8_fromcstr(histstr);
+ c->prompt_index = utf8_strlen(c->prompt_buffer);
c->flags |= CLIENT_STATUS;
break;
case MODEKEYEDIT_PASTE:
@@ -1069,20 +1132,28 @@ status_prompt_key(struct client *c, key_code key)
bufdata = paste_buffer_data(pb, &bufsize);
for (n = 0; n < bufsize; n++) {
ch = (u_char)bufdata[n];
- if (ch < 32 || ch == 127)
+ if (ch < 32 || ch >= 127)
break;
}
- c->prompt_buffer = xrealloc(c->prompt_buffer, size + n + 1);
+ c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1,
+ sizeof *c->prompt_buffer);
if (c->prompt_index == size) {
- memcpy(c->prompt_buffer + c->prompt_index, bufdata, n);
+ for (idx = 0; idx < n; idx++) {
+ ud = &c->prompt_buffer[c->prompt_index + idx];
+ utf8_set(ud, bufdata[idx]);
+ }
c->prompt_index += n;
- c->prompt_buffer[c->prompt_index] = '\0';
+ c->prompt_buffer[c->prompt_index].size = 0;
} else {
memmove(c->prompt_buffer + c->prompt_index + n,
c->prompt_buffer + c->prompt_index,
- size + 1 - c->prompt_index);
- memcpy(c->prompt_buffer + c->prompt_index, bufdata, n);
+ (size + 1 - c->prompt_index) *
+ sizeof *c->prompt_buffer);
+ for (idx = 0; idx < n; idx++) {
+ ud = &c->prompt_buffer[c->prompt_index + idx];
+ utf8_set(ud, bufdata[idx]);
+ }
c->prompt_index += n;
}
@@ -1093,42 +1164,55 @@ status_prompt_key(struct client *c, key_code key)
if (idx < size)
idx++;
if (idx >= 2) {
- swapc = c->prompt_buffer[idx - 2];
- c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1];
- c->prompt_buffer[idx - 1] = swapc;
+ utf8_copy(&tmp, &c->prompt_buffer[idx - 2]);
+ utf8_copy(&c->prompt_buffer[idx - 2],
+ &c->prompt_buffer[idx - 1]);
+ utf8_copy(&c->prompt_buffer[idx - 1], &tmp);
c->prompt_index = idx;
c->flags |= CLIENT_STATUS;
}
break;
case MODEKEYEDIT_ENTER:
- if (*c->prompt_buffer != '\0')
- status_prompt_add_history(c->prompt_buffer);
- if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0)
+ s = utf8_tocstr(c->prompt_buffer);
+ if (*s != '\0')
+ status_prompt_add_history(s);
+ if (c->prompt_callbackfn(c->prompt_data, s) == 0)
status_prompt_clear(c);
+ free(s);
break;
case MODEKEYEDIT_CANCEL:
if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
status_prompt_clear(c);
break;
case MODEKEY_OTHER:
- if (key <= 0x1f || key >= 0x7f)
+ if (key <= 0x1f || key >= KEYC_BASE)
+ break;
+ if (utf8_split(key, &tmp) != UTF8_DONE)
break;
- c->prompt_buffer = xrealloc(c->prompt_buffer, size + 2);
+
+ c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2,
+ sizeof *c->prompt_buffer);
if (c->prompt_index == size) {
- c->prompt_buffer[c->prompt_index++] = key;
- c->prompt_buffer[c->prompt_index] = '\0';
+ utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
+ c->prompt_index++;
+ c->prompt_buffer[c->prompt_index].size = 0;
} else {
memmove(c->prompt_buffer + c->prompt_index + 1,
c->prompt_buffer + c->prompt_index,
- size + 1 - c->prompt_index);
- c->prompt_buffer[c->prompt_index++] = key;
+ (size + 1 - c->prompt_index) *
+ sizeof *c->prompt_buffer);
+ utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
+ c->prompt_index++;
}
if (c->prompt_flags & PROMPT_SINGLE) {
- if (c->prompt_callbackfn(c->prompt_data,
- c->prompt_buffer) == 0)
+ s = utf8_tocstr(c->prompt_buffer);
+ if (strlen(s) != 1)
+ status_prompt_clear(c);
+ else if (c->prompt_callbackfn(c->prompt_data, s) == 0)
status_prompt_clear(c);
+ free(s);
}
c->flags |= CLIENT_STATUS;
@@ -1247,7 +1331,7 @@ status_prompt_complete_prefix(const char **list, u_int size)
/* Complete word. */
static char *
-status_prompt_complete(struct session *sess, const char *s)
+status_prompt_complete(struct session *session, const char *s)
{
const char **list = NULL, *colon;
u_int size = 0, i;
@@ -1300,7 +1384,7 @@ status_prompt_complete(struct session *sess, const char *s)
colon = "";
if (*s == ':') {
- RB_FOREACH(wl, winlinks, &sess->windows) {
+ RB_FOREACH(wl, winlinks, &session->windows) {
xasprintf(&tmp, ":%s", wl->window->name);
if (strncmp(tmp, s, strlen(s)) == 0){
list = xreallocarray(list, size + 1,
diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h
index 65e86dfae6a..4a615ba1e20 100644
--- a/usr.bin/tmux/tmux.h
+++ b/usr.bin/tmux/tmux.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.650 2016/10/10 17:28:30 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.651 2016/10/11 07:11:40 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1300,7 +1300,7 @@ struct client {
TAILQ_HEAD(, message_entry) message_log;
char *prompt_string;
- char *prompt_buffer;
+ struct utf8_data *prompt_buffer;
size_t prompt_index;
int (*prompt_callbackfn)(void *, const char *);
void (*prompt_freefn)(void *);
@@ -2345,6 +2345,8 @@ enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *);
enum utf8_state utf8_split(wchar_t, struct utf8_data *);
int utf8_strvis(char *, const char *, size_t, int);
char *utf8_sanitize(const char *);
+size_t utf8_strlen(const struct utf8_data *);
+u_int utf8_strwidth(const struct utf8_data *, ssize_t);
struct utf8_data *utf8_fromcstr(const char *);
char *utf8_tocstr(struct utf8_data *);
u_int utf8_cstrwidth(const char *);
diff --git a/usr.bin/tmux/utf8.c b/usr.bin/tmux/utf8.c
index cf7c85419ba..4f0ac8afe8e 100644
--- a/usr.bin/tmux/utf8.c
+++ b/usr.bin/tmux/utf8.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: utf8.c,v 1.33 2016/05/27 22:57:27 nicm Exp $ */
+/* $OpenBSD: utf8.c,v 1.34 2016/10/11 07:11:40 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -236,6 +236,33 @@ utf8_sanitize(const char *src)
return (dst);
}
+/* Get UTF-8 buffer length. */
+size_t
+utf8_strlen(const struct utf8_data *s)
+{
+ size_t i;
+
+ for (i = 0; s[i].size != 0; i++)
+ /* nothing */;
+ return (i);
+}
+
+/* Get UTF-8 string width. */
+u_int
+utf8_strwidth(const struct utf8_data *s, ssize_t n)
+{
+ ssize_t i;
+ u_int width;
+
+ width = 0;
+ for (i = 0; s[i].size != 0; i++) {
+ if (n != -1 && n == i)
+ break;
+ width += s[i].width;
+ }
+ return (width);
+}
+
/*
* Convert a string into a buffer of UTF-8 characters. Terminated by size == 0.
* Caller frees.